指令(Directive)能夠說是 AngularJS
的核心,而其開發也是比較困難的,本文主要介紹指令的一些參數和scope的綁定策略。html
從 AngularJS 的官方文檔中看到指令的參數以下:ajax
{ priority: 0, template: '<div></div>', // or // function(tElement, tAttrs) { ... }, // or // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, transclude: false, restrict: 'A', scope: false, controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, controllerAs: 'stringAlias', require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope, iElement, iAttrs, controller) { ... }, post: function postLink(scope, iElement, iAttrs, controller) { ... } } // or // return function postLink( ... ) { ... } }, // or // link: { // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, // post: function postLink(scope, iElement, iAttrs, controller) { ... } // } // or // link: function postLink( ... ) { ... } }
下面詳細講解每一個參數。緩存
priority(Number)
指令執行的優先級,用於多個指令同時做用於同一個元素時。例如:cookie
<select> <option ng-repeat="i in [1, 2]" ng-bind="i"></option> </select>
上面的例子中,ng-repeat
指令和 ng-bind
指令同時做用於 option
元素,因爲 ng-repeat
的 priority 爲1000,ng-bind
的 priority 爲0,所以先執行 ng-repeat
,而後變量 i 的值才能用於 ng-bind 中。app
template(String or Function)
HTML模板內容,用於下列狀況之一:函數
替換元素的內容(默認狀況)。post
替換元素自己(若是 replace 選項爲 true)。ui
將元素的內容包裹起來(若是 transclude 選項爲 true,後面會細說)。url
值能夠是:spa
一個 HTML 字符串。例如:<div>my name is {{name}}</div>
。
一個函數,接收兩個參數 tElement(元素自己) 和 tAttrs(元素的屬性集合),返回 HTML 字符串。
templateUrl(String or Function)
templateUrl 和 template 做用相同,但模板內容是從 $templateCache 服務或遠程 url 加載。
值能夠是:
一個字符串,AngularJS
會先從 $templateCache
中查找是否緩存了對應值,若是沒有則嘗試 ajax 加載。例如:在頁面中有以下 script:
<script type="text/ng-template" id="Hello.html"> <p>Hello</p> </script>
AngularJS 會將 type="text/ng-template"
的 script
標籤中的內容以 id 值爲 key 緩存到 $templateCache 服務中,此時能夠設置 templateUrl: 'Hello.html'
。
一個函數,接收兩個參數 tElement(元素自己) 和 tAttrs(元素的屬性集合),返回 url 地址。
transclude(Boolean)
官方文檔的解釋爲:編譯元素的內容,使其在指令內部可用。該選項通常和 ng-transclude
指令一塊兒使用。
若是 transclude 設置爲 true,則元素的內容會被放到模板中設置了 ng-transclude
指令的元素中。例如:
app.directive('testTransclude', [ function () { return { restrict: 'E', transclude: true, template: '<div>\ <p>指令內部段落</p>\ <div ng-transclude></div>\ </div>' }; } ]);
<test-transclude> <p>該段落會被放到指令內部</p> </test-transclude>
上面生成後的 DOM 結構爲:
restrict(String)
指令的使用形式。
值能夠爲:
'E'
- 指令做爲元素使用
'A'
- 指令做爲屬性使用
'C'
- 指令做爲類名使用
'M'
- 指令做爲註釋使用(不經常使用)
能夠是以上值的組合,如 restrict: 'EA'
表示指令既能夠做爲屬性使用,也能夠做爲元素使用。
scope(Boolean or Object)
關於 scope 選項將會在後面的指令 scope 中細說。
controller(Function)
通常狀況下不須要使用指令的 controller
,只要使用 link
就夠了,後面會細說 link 函數。
用 controller
的場景是該指令(a)會被其餘指令(b)require
的時候,在 b 的指令裏能夠傳入 a 的這個 controller,目的是爲了指令間的複用和交流。而 link
只能在指令內部中定義行爲,沒法作到這樣。
controllerAs(String)
爲控制器指定別名,這樣能夠在須要控制器的地方使用該名字進行注入。
require(String or Array)
表示指令依賴於一個或多個指令,並注入所依賴指令的控制器到 link
函數的第四個參數中。若是所依賴的指令不存在,或所依賴指令的控制器不存在則會報錯。
依賴名稱前綴能夠爲:
(沒有前綴) - 在當前元素中查找依賴指令的控制器,若是不存在則報錯。
?
- 在當前元素中查找依賴指令的控制器,若是不存在傳 null
到 link
中。
^
- 在當前元素及父元素中查找依賴指令的控制器,若是不存在則報錯。
?^
- 在當前元素及父元素中查找依賴指令的控制器,若是不存在傳 null
到 link
中。
例子:
app.directive('validate', [ function () { return { restrict: 'A', require: 'ngModel', link: function (scope, ele, attrs, ngModelCtrl) { // 監聽值變化 ngModelCtrl.$viewChangeListeners.push(function () { scope.validateResult = ngModelCtrl.$viewValue === 'Heron'; }); } }; } ]); app.controller('myCtrl', [ '$scope', '$cookieStore', function ($scope, $cookieStore) { $scope.name = 'Heron'; $scope.sayHi = function (name, age) { alert('Hello ' + name + ', your age is ' + age); } } ]);
<div ng-controller="myCtrl"> <input type="text" ng-model="name" validate> <p> validate 結果:{{validateResult}} </p> </div>
運行結果如圖:
compile(Function)
和 link(Function)
建立的建立過程能夠分爲編譯(compile)階段和連接(link)階段,所以二者放一塊兒講。
二者區別在於:
compile
函數的做用是對指令的模板進行轉換。
link
函數的做用是在視圖和模型之間創建關聯,包括註冊事件監聽函數和更新 DOM
操做。
scope
在連接階段纔會被綁定到元素上,所以 compile
函數中沒有入參 scope
。
對於同一個指令的多個示例,compile
函數只會執行一次,而 link
函數在每一個實例中都會執行。
若是自定義了 compile
函數,則自定義的 link
函數 無效,而是使用 compile
函數 返回的 link
函數。
scope 選項有三種值:
false
- 使用父 scope
。改變父 scope
會影響指令 scope
,反之亦然。
true
- 繼承父 scope
,並建立本身的 scope
。改變父 scope
會影響指令 scope
,而改變指令 scope
不會影響父 scope
。
{}
- 不繼承父 scope
,建立獨立的 scope
。若是不使用雙向綁定策略(後面會講),改變父 scope
不會影響指令 scope
,反之亦然。
例子:
app.controller('myCtrl', [ '$scope', '$cookieStore', function ($scope, $cookieStore) { $scope.scopeFalse = 'Heron'; $scope.scopeTrue = 'Heron'; $scope.scopeObject = 'Heron'; } ]); app.directive('directiveFalse', [ function () { return { restrict: 'EA', scope: false, template: '<div>\ <p>\ <span>指令 scope: </span>\ <input type="text" ng-model="scopeFalse">\ </p>\ </div>' }; } ]); app.directive('directiveTrue', [ function () { return { restrict: 'EA', scope: true, template: '<div>\ <p>\ <span>指令 scope: </span>\ <input type="text" ng-model="scopeTrue">\ </p>\ </div>' }; } ]); app.directive('directiveObject', [ function () { return { restrict: 'EA', scope: {}, template: '<div>\ <p>\ <span>指令 scope: </span>\ <input type="text" ng-model="scopeObject">\ </p>\ </div>', link: function (scope) { // 因爲使用獨立scope,所以須要本身定義變量 scope.scopeObject = 'Heron'; } }; } ]);
<div ng-controller="myCtrl"> <h3>scope: false</h3> <p> <span>父 scope: </span> <input type="text" ng-model="scopeFalse"> </p> <directive-false></directive-false> <h3>scope: true</h3> <p> <span>父 scope: </span> <input type="text" ng-model="scopeTrue"> </p> <directive-true></directive-true> <h3>scope: {}</h3> <p> <span>父 scope: </span> <input type="text" ng-model="scopeObject"> </p> <directive-object></directive-object> </div>
運行結果如圖:
針對獨立 scope,能夠經過在對象中聲明如何從外部傳入參數。有如下三種綁定策略:
@
- 使用 DOM 屬性值單項綁定到指令 scope
中。此時綁定的值老是一個字符串,由於 DOM
的屬性值是一個字符串。
<div my-directive age="26"></div> scope: { age: '@' }
=
- 在父 scope
和指令 scope
之間創建雙向綁定。
<div my-directive age="age"></div> scope: { age: '=' }
&
- 使用父 scope
的上下文執行函數。通常用於綁定函數。
<div my-directive sayHi="sayHi()"></div> scope: { sayHi: '&' }
綁定函數時,有時須要向指令外部傳遞參數,以下:
app.controller('myCtrl', [ '$scope', '$cookieStore', function ($scope, $cookieStore) { $scope.name = 'Heron'; $scope.sayHi = function (name, age) { alert('Hello ' + name + ', your age is ' + age); }; } ]); app.directive('myDirective', [ function () { return { restrict: 'E', replace: true, scope: { clickMe: '&' }, template: '<div>\ <button class="btn btn-info" ng-click="clickMe({ age: age })">點我</button>\ </div>', link: function (scope) { scope.age = 26; } }; } ]);
<div ng-controller="myCtrl"> <my-directive click-me="sayHi(name, age)"></my-directive> </div>
運行結果如圖:
說明一下:首先聲明 clickMe: '&'
使用父 scope 的環境執行 clickMe 函數,而後在傳遞給指令時聲明 click-me="sayHi(name, age)"
,表示父 scope 的 sayHi
方法須要兩個參數,一個是 name,一個是 age,而後再指令中使用對象 {} 的方式向外傳遞參數,如 ng-click="clickMe({ age: age })"
,表示向指令外傳遞 age 參數,sayHi 方法從指令拿到 age 參數,再從本身的上下文中拿到 name 參數。
AngularJS 指令的開發和使用變幻無窮,也有許多坑,但願你們留意,也但願你們能在評論區多多交流心得。