Directive將一段html,js封裝在一塊兒,造成一個能夠複用的獨立個體,具備特定的功能。angularjs中的指令一般是比較小的組件,它至關因而給咱們提供了一些公共的自定義的DOM元素、class屬性或attr屬性。除此以外,咱們能夠在這個基礎上來操做scope,綁定事件,更改樣式等等。經過Directive咱們能夠封裝不少公共指令,好比分頁、自動補全等多個指令,封裝好後,咱們下次複用只要在html頁面上添加該指令就能夠實現複雜的功能。
總結一下使用Directive的場景:
1.抽象一個自定義組件,在其餘地方重用。
2.使html更具語義化,不須要深刻研究代碼和邏輯就能夠知道頁面實現了哪些功能。
angular封裝了不少本身的指令,都是ng開頭,好比ng-model,ng-controller,ng-show,ng-click等等,咱們也能夠封裝本身的指令,在當前的節點上乾點特別的事。javascript
html部分:html
<body ng-app="testApp"> <div ng-controller="testController"> <input type="text" ng-model="city" placeholder="Enter a city" /> <my-test ng-model="city" ></my-test> <span my-test="exp" ng-model="city"></span> <span ng-model="city"></span> </div> </body>
js部分java
var app = angular.module('testApp', [ 'directives', 'controllers' ]); // 自定義directive var myDirective = angular.modeule('directives', []); myDirective.directive('myTest', function() { return { restrict: 'EMAC', require: '^ngModel', scope: { ngModel: '=' }, template: '<div><h4>Weather for {{ngModel}}</h4</div>' }; }); // 定義controller var myControllers = angular.module('controllers', []); myControllers.controller('testController', [ '$scope', function($scope) { $scope.name = 'this is directive1'; } ]);
以上是一個自定義myTest這個directive的應用。來看一下定義一個directive能夠用哪些格式和參數:angularjs
var myDirective = angular.module('directives', []); myDirective.directive('directiveName', function() { return { template: '<div></div>', replace: false, transclude: true, restrict: 'E', scope: {}, controller: function($scope, $element) { }, complie: function(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { }, post: function postLink(scope, iElement, iAttrs, controller) { } }; }, link: function(scope, iElement, iAttrs) { } }; });
咱們能夠發現directive最關鍵的部分就是經過return來設置參數,那麼return裏能夠設置哪些參數呢,下面具體講一講這些參數及其說明。
1.name
當前scope的名稱,通常聲明時使用默認值,不用手動設置此屬性。
2.priority
優先級。當有多個directive定義在同一個DOM元素上時,有時要明確他們的執行順序。若是優先級相同,那麼執行順序不肯定(通常狀況下,優先級高的先執行,相同優先級的按照先綁定後執行)。
3.teminal
最後一組。若是設置爲true,那麼當前的priority將會成爲最後一組執行的directive。也就是說,比這個directive的priority更低的directive將不會執行,同優先級的依然會執行,但執行順序不肯定。
4.scope
(1)true
表示爲這個directive建立一個新的scope。若是在同一個元素中有多個directive須要新的scope的話,它仍是隻會建立一個scope。
(2){}
將會建立一個新的,獨立的scope,這個scope與通常的scope的區別在於它不是經過原型繼承父scope的。這對可複用的組件而言頗有用,能夠有效的防止讀取或者修改父級scope的數據。
5.controller
controller容許其餘directive經過指定名稱的require進行共享,也就是說directive之間能夠相互溝通,加強相互之間的行爲。controller默認注入如下本地對象:
$scope:與當前元素結合的scope
$element:當前元素
$attrs:當前元素的屬性對象
$transclude:一個預先綁定到當前scope的轉置linking function
6.require
請求另外的controller,傳入當前directive的linking function中,require須要傳入一個directive controller的名稱,若是沒有對應的controller會拋出一個error。名稱能夠加入如下前綴:
?: 不要拋出異常,這就使得這個依賴變爲一個可選項
^: 容許查找父元素的controller 瀏覽器
7.restrict
EACM的子集的字符串,限制了directive爲指定的聲明方式。省略的話,directive將牢牢容許經過屬性聲明的方式:
E:元素
A:屬性
C:class名
M:註釋
比較經常使用的是EA
8.template
若是replace爲true,則將模板內容替換當前的html元素,並將原來元素的屬性,class一併轉移;若是replace爲false,則將模板元素做爲當前元素的子元素。
9.templateUrl
與template基本一致,但模板經過指定的url進行加載。因爲模板加載是異步的,全部compilation,linking都會暫停,等加載完畢後再執行。
10.replace
設置爲true,那麼模板將會替換當前元素,而不是做爲子元素添加到當前元素中。(爲true時模板必需要有一個根節點)
11.transclude
編譯元素的內容,使它可以被directive使用。須要在模板中配合ngTransclude使用。
12.compile
13.linkapp
看一個transclude的例子dom
<body> <hello> <br/><span>原始的內容,</span><br/> <span>還會在這裏。</span> </hello> <hello> </hello> </body>
var appModule = angular.module('app', []); appModule.directive('hello', function() { return { restrict: 'E', template: '<div>Hi there <span ng-transclude></span></div>', transclude: true }; });
能夠發現,這裏的ng-transclude會把hello標籤裏內容讀取出來,若是transclude設置爲false,那麼瀏覽器就不會處理hello這個標籤裏內容。
注意:若是指令使用了transclude參數,那麼控制器就沒法正常監聽數據模型的變化。建議在連接函數裏使用$watch服務。異步
接下來重點講一下scope。
scope主要是用來隔離做用域的,那麼scope在true,false,{}時值是如何變化的呢,看下面一個例子:ide
<body> <div ng-controller='MainController'> 父親:{{name}}<input ng-model="name" /> <div my-directive></div> </div> </body>
<script type="text/javascript"> var app = angular.module('myApp', []); app.controller('MainController', function ($scope) { $scope.name = '林炳文'; }); app.directive('myDirective', function () { return { restrict: 'EA', scope:false, template: '<div>兒子:{{ name }}<input ng-model="name"/></div>' }; }); </script>
scope:false時,子scope繼承父scope的值,改變父親的值,兒子的值也會改變,反之同樣(繼承不隔離)
scope:true時,子scope繼承父scope的值,改變父親的值,兒子的值也會改變。可是改變兒子的值,父親的值不變(繼承隔離)
scope:{}時,沒有繼承父scope,改變任何一方不會改變另外一方的值(不繼承隔離) 函數
當你想要建立一個可重用的組件時,隔離做用域是一個很好的選擇,這樣能夠防止父做用域被污染。那麼隔離做用域後如何訪問父級做用域呢?
angular經過綁定策略來訪問父做用域的屬性。
directive提供了三種方法同隔離以外的地方進行交互:
(1) @
主要經過directive所在的標籤屬性綁定外部字符串值。這種綁定是單向的,即父scope變化,directive中的scope的對應屬性也會變化。可是隔離scope中的綁定變化,父scope是不知道的。
<div ng-controller="myController"> <div class="result"> <div>父scope: <div>Say:{{name}}<br>改變父scope的name:<input type="text" value="" ng-model="name"/></div> </div> <div>隔離scope: <div isolated-directive name="{{name}}"></div> </div> <div>隔離scope(不使用父scope {{name}}): <div isolated-directive name="name"></div> </div> </div> </div>
var app = angular.module('myApp', []); app.controller("myController", function ($scope) { $scope.name = "hello world"; }).directive("isolatedDirective", function () { return { scope: { name: "@" }, template: 'Say:{{name}} <br>改變隔離scope的name:<input type="buttom" value="" ng-model="name" class="ng-pristine ng-valid">' }; });
(2) =
經過directive的attr屬性在局部scope和父scope的屬性名之間創建雙向綁定
(3) &
directive在父scope的上下文中執行一個表達式,此表達式是一個function。directive能夠經過一個父scope中的function,當directive中有什麼動做須要更新到父scope中的時候,能夠在父scope上下文中執行一段代碼或一個函數。
<div ng-controller="myController"> <div>父scope: <div>Say:{{value}}</div> </div> <div>隔離scope: <div isolated-directive action="click()"></div> </div> </div>
var app = angular.module('myApp', []); app.controller("myController", function ($scope) { $scope.value = "hello world"; $scope.click = function () { $scope.value = Math.random(); }; }).directive("isolatedDirective", function () { return { scope: { action: "&" }, template: '<input type="button" value="在directive中執行父scope定義的方法" ng-click="action()"/>' } })
再看一個scope應用的例子
<body ng-app="testApp"> <div ng-controller="attrtest"> <my-attr info="naomi"></my-attr> </div> </body>
myDirectives.directive('myAttr', function() { return { restrict: 'E', scope: { customerInfo: '=info' }, template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' + 'Name: {{vojta.name}} Address: {{vojta.address}}' }; }); myControllers.controller('attrtest',['$scope', function($scope) { $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' }; $scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' }; } ]);
結果:
Name: Address:
Name: Vojta Address: 3456 Somewhere Else
接下來說一下require
<body> <outer-directive> <inner-directive></inner-directive> <inner-directive2></inner-directive2> </outer-directive> <script> var app = angular.module('myApp', []); app.directive('outerDirective', function() { return { scope: {}, restrict: 'AE', controller: function($scope) { this.say = function(someDirective) { console.log('Got:' + someDirective.message); }; } }; }); app.directive('innerDirective', function() { return { scope: {}, restrict: 'AE', require: '^outerDirective', link: function(scope, elem, attrs, controllerInstance) { scope.message = "Hi,leifeng"; controllerInstance.say(scope); } }; }); app.directive('innerDirective2', function() { return { scope: {}, restrict: 'AE', require: '^outerDirective', link: function(scope, elem, attrs, controllerInstance) { scope.message = "Hi,shushu"; controllerInstance.say(scope); } }; }); </script> </body>
結果:
Got:Hi,leifeng
Got:Hi,shushu
上例中的innerDirective和innerDirective2複用了outerDirective的controller中的方法。這也進一步說明,指令中的controller是用來讓不一樣指令間通訊用的。
require的值是另外一個指令的名稱,但實際上引用的是那個指令的控制器實例。require很是有用,由於不少時候指令之間是要相互配合的,好比說require:'ngModel',那麼當angular初始化它的時候,就會在它所在的元素上尋找一個叫作ng-model的指令,而後取得它的控制器實例。找到時候,就能夠把這個控制器的實例做爲Link函數的第四個參數傳進來。
關於require:'ngModel'其餘的一些用法,會在directive二講到。
再說說controller,link,compile之間的關係
myController.controller('directive2',[ '$scope', function($scope) { $scope.number = '1111 '; } ]); myDirective.directive('exampleDirective', function() { return { restrict: 'E', template: '<p>Hello {{number}}!</p>', controller: function($scope, $element){ $scope.number = $scope.number + "22222 "; }, link: function(scope, el, attr) { scope.number = scope.number + "33333 "; }, compile: function(element, attributes) { return { pre: function preLink(scope, element, attributes) { scope.number = scope.number + "44444 "; }, post: function postLink(scope, element, attributes) { scope.number = scope.number + "55555 "; } }; } } });
<body ng-app="testApp"> <div ng-controller="directive2"> <example-directive></example-directive> </div> </body>
看一下結果:
Hello 1111 22222 44444 5555 !
從結果能夠看出,controller先運行,compile後運行,link不運行。
注掉compile,結果是:
Hello 1111 22222 33333 !
此次是controller先運行,link後運行。
也就是說,link和compile不兼容,通常的,compile比link的優先級要高。
注意,在controller和link中均可以定義方法,它們的區別是,controller主要是用來提供可在指令間複用的行爲,但link連接函數只能在當前內部指令中定義行爲,沒法再指令複用。