天天看點

AngularJS中自定義指令

       AngularJS中除了内置指令,還可以自定義指令。自定義指令和自定義過濾器一樣,有兩種方法:

第一種,在module中配置:$compileProvider.directive('directiveName', function(){ });

       代碼模版為:

<code>    </code><code>$compileProvider.directive('', ['', function(){</code>

<code>    </code><code>// Runs during compile</code>

<code>    </code><code>return {</code>

<code>        </code><code>// name: '',</code>

<code>        </code><code>// priority: 1,</code>

<code>        </code><code>// terminal: true,</code>

<code>        </code><code>// scope: {}, // {} = isolate, true = child, false/undefined = no change</code>

<code>        </code><code>// controller: function($scope, $element, $attrs, $transclude) {},</code>

<code>        </code><code>// require: 'ngModel', // Array = multiple requires, ? = optional, ^ = check parent elements</code>

<code>        </code><code>// restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment</code>

<code>        </code><code>// template: '',</code>

<code>        </code><code>// templateUrl: '',</code>

<code>        </code><code>// replace: true,</code>

<code>        </code><code>// transclude: true,</code>

<code>        </code><code>// compile: function(tElement, tAttrs, function transclude(function(scope, cloneLinkingFn){ return function linking(scope, elm, attrs){}})),</code>

<code>        </code><code>link: function($scope, iElm, iAttrs, controller) {</code>

<code>            </code> 

<code>        </code><code>}</code>

<code>    </code><code>};</code>

       第二種,.directive('directiveName', function(){ });

<code>        </code><code>.directive('', ['', function(){</code>

<code>            </code><code>// Runs during compile</code>

<code>            </code><code>return {</code>

<code>                </code><code>// name: '',</code>

<code>                </code><code>// priority: 1,</code>

<code>                </code><code>// terminal: true,</code>

<code>                </code><code>// scope: {}, // {} = isolate, true = child, false/undefined = no change</code>

<code>                </code><code>// controller: function($scope, $element, $attrs, $transclude) {},</code>

<code>                </code><code>// require: 'ngModel', // Array = multiple requires, ? = optional, ^ = check parent elements</code>

<code>                </code><code>// restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment</code>

<code>                </code><code>// template: '',</code>

<code>                </code><code>// templateUrl: '',</code>

<code>                </code><code>// replace: true,</code>

<code>                </code><code>// transclude: true,</code>

<code>                </code><code>// compile: function(tElement, tAttrs, function transclude(function(scope, cloneLinkingFn){ return function linking(scope, elm, attrs){}})),</code>

<code>                </code><code>link: function($scope, iElm, iAttrs, controller) {</code>

<code>                    </code> 

<code>                </code><code>}</code>

<code>            </code><code>};</code>

<code>        </code><code>}]);</code>

  可以看到,定義指令會傳回一個對象,這個對象裡面包含了各個屬性(選項),這些屬性(選項)就是用來定義指令的。

     指令的名字不要和内置指令沖突,如果指令的名字為xxx-yyy,那麼設定指令的名字時應為xxxYyy,即駝峰式的命名。

restrict: 描述指令在模版中的使用方式,包括:元素、樣式類、屬性、注釋,或者以上幾種方式的任意組合。

<a href="http://s3.51cto.com/wyfs02/M01/6F/2B/wKiom1WThHaAx7B6AANtxICdXuk880.bmp" target="_blank"></a>

template: 以字元串的形式編寫一個内聯模闆。

templateUrl: 加載模版所需要使用的url,如果已經指定了template,此屬性會被忽略。

replace: 如果該屬性為true,則替換指令所在的元素;如果為false或者不指定,則追加到元素内部。

例子:

<code>&lt;!DOCTYPE html&gt;</code>

<code>&lt;</code><code>html</code> <code>ng-app</code><code>=</code><code>"firstMoudule"</code><code>&gt;</code>

<code>&lt;</code><code>head</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>meta</code> <code>charset</code><code>=</code><code>'utf-8'</code><code>&gt;</code>

<code>&lt;/</code><code>head</code><code>&gt;</code>

<code>&lt;</code><code>body</code> <code>ng-controller</code><code>=</code><code>"firstController"</code><code>&gt;</code>

<code>    </code><code>&lt;!-- 使用自定義指令first-tag --&gt;</code>

<code>    </code><code>&lt;</code><code>div</code> <code>first-tag&gt;&lt;/</code><code>div</code><code>&gt;</code>

<code>    </code> 

<code>    </code><code>&lt;</code><code>script</code> <code>src</code><code>=</code><code>"http://cdn.bootcss.com/angular.js/1.4.0-rc.2/angular.min.js"</code><code>&gt;&lt;/</code><code>script</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>script</code> <code>type</code><code>=</code><code>"text/javascript"</code><code>&gt;</code>

<code>    </code><code>angular.module('firstMoudule', [], function($compileProvider, $controllerProvider) {</code>

<code>        </code><code>$compileProvider.directive('firstTag', function() {</code>

<code>                </code><code>restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment</code>

<code>                </code><code>template: '&lt;</code><code>div</code><code>&gt;hello pomelo!&lt;/</code><code>div</code><code>&gt;',</code>

<code>                </code><code>replace: true</code>

<code>        </code><code>});</code>

<code>        </code><code>$controllerProvider.register('firstController', function() {});</code>

<code>    </code><code>});</code>

<code>    </code><code>&lt;/</code><code>script</code><code>&gt;</code>

<code>&lt;/</code><code>body</code><code>&gt;</code>

<code>&lt;/</code><code>html</code><code>&gt;</code>

transclude: 當此屬性為true時,把指令元素中原來的子節點移動到一個新模闆的内部。

<code>    </code><code>&lt;</code><code>div</code> <code>first-tag&gt;</code>

<code>        </code><code>old data         </code>

<code>    </code><code>&lt;/</code><code>div</code><code>&gt;</code>

<code>                </code><code>/*transclude為true時,old data會被放到具有ng-transclude屬性的地方,也就是下面的span*/</code>

<code>                </code><code>template: '&lt;</code><code>div</code><code>&gt;new data &lt;</code><code>span</code> <code>ng-transclude&gt;&lt;/</code><code>span</code><code>&gt; &lt;/</code><code>div</code><code>&gt;',</code>

<code>                </code><code>replace: true,</code>

<code>                </code><code>transclude: true</code>

輸出

<a href="http://s3.51cto.com/wyfs02/M01/6F/29/wKioL1WTlxazSiMIAAAPg6Gm0FI926.jpg" target="_blank"></a>

priority: 設定指令在模闆中的優先級,用整數來表示,數字大的優先級高,先執行。執行順序是相對于元素上的其它指令而言的。如果兩個指令的該值相同,則先定義的先執行。比如内置的ng-repeat該值為1000。

terminal: 和priority配合使用。如果此屬性為true,那麼priority比它小的都不會再執行。

<code>    </code><code>&lt;!-- 同時使用兩個指令 --&gt;</code>

<code>    </code><code>&lt;</code><code>div</code> <code>first-tag second-tag&gt;&lt;/</code><code>div</code><code>&gt;</code>

<code>                </code><code>restrict: 'A',</code>

<code>                </code><code>priority: 10</code>

<code>        </code><code>$compileProvider.directive('secondTag', function() {</code>

<code>                </code><code>template: '&lt;</code><code>div</code><code>&gt;data&lt;/</code><code>div</code><code>&gt;',</code>

<code>                </code><code>transclude: true,</code>

<code>                </code><code>priority: 20,</code>

<code>                </code><code>terminal: true</code>

  注意,這裡同時使用兩個指令,隻能有一個裡面template有内容,否則将出錯。second-tag優先級較高,先執行,并且terminal為true,first-tag不會執行。

complie、link:雖然template的方式很有用,但對于指令來說,真正有趣的發生在complie和link函數中。這兩個函數是根據Angular建立動态視圖的兩個處理階段來命名的。Angular的初始化過程為:

    1.加載腳本 加載Angular庫,查找ng-app指令,進而找到應用的邊界。

    2.編譯階段 周遊DOM結構,辨別出模版中注冊的所有指令。對于每一條指令,如果存在complie函數,則調用complie函數得到一個編譯好的template函數,template函數又會調用從所有指令收集來的link函數。編譯階段就是負責模闆的轉換。

    3.連結階段 為了讓視圖變成動态的,Angular會對每一條指令運作一個link函數。link函數負責在model和view之間進行動态關聯。

    complie函數僅僅在編譯階段運作一次,而link函數對于指令的每一個執行個體,都會執行一次。

    對于我們會編寫的大多數指令來說,并不需要對模闆轉換,隻有編寫link函數即可。有complie函數就不用再定義link函數了。

    complie函數的文法為:

    這裡傳回的相當于link函數。

<code>compile: function(tElement, tAttrs,transclude) {</code>

<code>         </code><code>return {</code>

<code>           </code><code>pre: function preLink() { },</code>

<code>           </code><code>post: function postLink() { }</code>

<code>          </code><code>};</code>

<code>}</code>

    tElement是目前指令所在的jQuery對象。tAttrs是指令上定義的參數,比如指令fisrt-tag="123",則tAttrs為123 。這裡transclude是一個函數,如果需要對内容進行變換,而簡單的基于模闆的變換并沒有提供這種功能,那麼可以自己寫這個函數。

    如果直接傳回,則傳回的是postLink,如下:

<code>         </code><code>return function() { };</code>

    preLink在編譯階段之後,指令連結子元素之前運作。postLink在所有的子元素指令都連結後才運作。如果需要修改DOM結構,應該在postLink裡面做這件事情,如果在preLink裡面做則會破壞綁定過程,并導緻錯誤。

例子

<code>&lt;</code><code>html</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>meta</code> <code>charset</code><code>=</code><code>"utf-8"</code><code>&gt;</code>

<code>&lt;</code><code>body</code> <code>ng-app</code><code>=</code><code>"app"</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>div</code> <code>ng-controller</code><code>=</code><code>"Controller1"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>div</code> <code>tag1 tag2&gt;&lt;/</code><code>div</code><code>&gt;</code>

<code>    </code><code>angular.module('app', [], function($compileProvider) {</code>

<code>            </code><code>$compileProvider.directive('tag1', function() {</code>

<code>                </code><code>return {</code>

<code>                    </code><code>restrict: 'A',</code>

<code>                    </code><code>template: '&lt;</code><code>div</code><code>&gt;hello pomelo!&lt;/</code><code>div</code><code>&gt;',</code>

<code>                    </code><code>replace: true,</code>

<code>                    </code><code>compile: function(tElement, tAttrs, transclude) {</code>

<code>                        </code><code>console.log('tag1 complie...');</code>

<code>                        </code><code>return {</code>

<code>                            </code><code>pre: function preLink() {</code>

<code>                                </code><code>console.log('tag1 preLink...');</code>

<code>                            </code><code>},</code>

<code>                            </code><code>post: function postLink() {</code>

<code>                                </code><code>console.log('tag1 postLink...');</code>

<code>                            </code><code>}</code>

<code>                        </code><code>};</code>

<code>                    </code><code>}</code>

<code>                </code><code>};</code>

<code>            </code><code>});</code>

<code>            </code><code>$compileProvider.directive('tag2', function() {</code>

<code>                    </code><code>replace: false,</code>

<code>                        </code><code>console.log('tag2 complie...');</code>

<code>                                </code><code>console.log('tag2 preLink...');</code>

<code>                                </code><code>console.log('tag2 postLink...');</code>

<code>        </code><code>})</code>

<code>        </code><code>.controller('Controller1', function() {});</code>

<a href="http://s3.51cto.com/wyfs02/M00/6F/2D/wKiom1WTql7gkvnqAAA6leZ1J9I581.jpg" target="_blank"></a>

controller、controllerAs、require:controller會暴露一個API,通過這個API可以在多個指令之間通過依賴注入進行通信。controllerAs是給controller起一個别名,友善使用。require可以将其它指令傳遞給自己。

<code>        </code><code>&lt;</code><code>div</code> <code>tag1&gt;&lt;/</code><code>div</code><code>&gt;</code>

<code>                    </code><code>controller: function($scope) {</code>

<code>                        </code><code>$scope.data = 'this is the data in controller';</code>

<code>                        </code><code>this.Data = 'some Data';</code>

<code>                    </code><code>},</code>

<code>                    </code><code>controllerlAs: 'Controller',</code>

<code>                    </code><code>link: function($scope, iElm, iAttrs, Controller) {</code>

<code>                        </code><code>console.log($scope.data);</code>

<code>                        </code><code>console.log(Controller.Data);</code>

<a href="http://s3.51cto.com/wyfs02/M01/6F/32/wKiom1WUlwTAwSXPAAA17Qx8XO0858.jpg" target="_blank"></a>

  在多個指令間通信還需要require方法。

<a href="http://s3.51cto.com/wyfs02/M01/6F/2F/wKioL1WUmf7DWC2TAARVnCycsyg274.bmp" target="_blank"></a>

<code>        </code><code>&lt;</code><code>div</code> <code>parent-tag&gt;&lt;/</code><code>div</code><code>&gt;</code>

<code>    </code><code>angular.module('app', [])</code>

<code>        </code><code>.directive('parentTag', function() {</code>

<code>                </code><code>restrict: 'ECMA',</code>

<code>                </code><code>template: '&lt;</code><code>div</code><code>&gt;&lt;</code><code>ul</code><code>&gt;&lt;</code><code>li</code> <code>ng-repeat</code><code>=</code><code>"i in players"</code><code>&gt;`i`.`name` `i`.`number`&lt;/</code><code>li</code><code>&gt;&lt;/</code><code>ul</code><code>&gt;&lt;</code><code>child-tag</code><code>&gt;&lt;/</code><code>child-tag</code><code>&gt;&lt;/</code><code>div</code><code>&gt;',</code>

<code>                </code><code>controller: function($scope) {</code>

<code>                    </code><code>$scope.players = [{</code>

<code>                        </code><code>name: 'Mertersacker',</code>

<code>                        </code><code>number: 4</code>

<code>                    </code><code>}, {</code>

<code>                        </code><code>name: 'Koscielny',</code>

<code>                        </code><code>number: 6</code>

<code>                        </code><code>name: 'Gabriel',</code>

<code>                        </code><code>number: 5</code>

<code>                    </code><code>}];</code>

<code>                    </code><code>this.addPlayer = function() {</code>

<code>                        </code><code>$scope.$apply(function() {</code>

<code>                            </code><code>$scope.players.push({</code>

<code>                                </code><code>name: 'Chambers',</code>

<code>                                </code><code>number: 21</code>

<code>                            </code><code>});</code>

<code>                        </code><code>});</code>

<code>                </code><code>},</code>

<code>                </code><code>controllerAs: 'parentController'</code>

<code>        </code><code>.directive('childTag', function() {</code>

<code>                </code><code>require: '^parentTag',</code>

<code>                </code><code>template: '&lt;</code><code>button</code><code>&gt;add player Chambers&lt;/</code><code>button</code><code>&gt;',</code>

<code>                </code><code>link: function($scope, iElm, iAttrs, parentController) {</code>

<code>                    </code><code>iElm.on('click', parentController.addPlayer);</code>

<code>            </code><code>}</code>

<a href="http://s3.51cto.com/wyfs02/M01/6F/33/wKiom1WUoaegncVvAABZmdV8hKE121.jpg" target="_blank"></a>

<a href="http://s3.51cto.com/wyfs02/M02/6F/30/wKioL1WUo23CU0CRAACA_qq9IZI359.jpg" target="_blank"></a>

scope:指明指令所操控資料的作用域。

    如果不指定,scope為false,會使用指令對應的DOM元素上存在的scope對象;

    如果scope為true,則會建立一個scope,它繼承了外層控制器中的scope,在繼承樹中,位于目前scope對象上方的所有scope對象的值都可以被讀取。

    如果scope為{attributeName:'bindingStratry',... ...}即一個對象時,會建立一個獨立的對象。

    對于scope是一個object的情況,有點複雜。此時scope的結構為:

    scope:{

           attributeName1:'&amp;bindingStratry1',

           attributeName2:'=bindingStratry2',

           attributeName3:'@bindingStratry3'

    }

    當為&amp;bindingStratry時,表示傳遞一個來自父scope的函數,稍後調用。

<code>        </code><code>&lt;</code><code>div</code> <code>my-tag </code><code>obj</code><code>=</code><code>"players"</code><code>&gt;&lt;/</code><code>div</code><code>&gt;</code>

<code>        </code><code>.directive('myTag', function() {</code>

<code>                    </code><code>console.log($scope.myScopeFn());//myScopeFn必須以函數方法使用</code>

<code>                </code><code>scope: {</code>

<code>                    </code><code>myScopeFn: '&amp;obj'</code>

<code>        </code><code>.controller('Controller1', function($scope) {</code>

<code>            </code><code>$scope.players = [{</code>

<code>                </code><code>name: 'Mertersacker',</code>

<code>                </code><code>number: 4</code>

<code>            </code><code>}, {</code>

<code>                </code><code>name: 'Koscielny',</code>

<code>                </code><code>number: 6</code>

<code>                </code><code>name: 'Gabriel',</code>

<code>                </code><code>number: 5</code>

<code>            </code><code>}];</code>

<a href="http://s3.51cto.com/wyfs02/M02/6F/35/wKiom1WUxCyQzkN5AACFMOeG70Q919.jpg" target="_blank"></a>

   當為=bindingStratry時,表示傳遞一個來自父scope的屬性,并且是和父scope中對應屬性雙向綁定的。

<code>        </code> 

<code>                    </code><code>&lt;!-- 雙向資料綁定,可以操縱父scope的資料,這裡添加一個元素進去 --&gt;</code>

<code>                    </code><code>$scope.myScopeAttr.push({</code>

<code>                        </code><code>name: 'Ozil',</code>

<code>                        </code><code>number: 11</code>

<code>                    </code><code>});</code>

<code>                    </code><code>myScopeAttr: '=obj'</code>

<code>            </code><code>console.log($scope.players);</code>

<a href="http://s3.51cto.com/wyfs02/M01/6F/36/wKiom1WUy2ywC1cuAACd61NMQnE554.jpg" target="_blank"></a>

    可以看到,父scope中players的資料改變了。=bindingStratry能雙向資料綁定。

    當為@bindingStratry時,表示讀取一個來自父scope的屬性,這個屬性隻讀,無法改變。

<code>        </code><code>&lt;!-- 這裡要特别注意,要放在{{ }}裡 --&gt;</code>

<code>        </code><code>&lt;</code><code>div</code> <code>my-tag </code><code>obj</code><code>=</code><code>"`players`"</code><code>&gt;&lt;/</code><code>div</code><code>&gt;</code>

<code>                    </code><code>console.log($scope.myScopeAttr);</code>

<code>                    </code><code>myScopeAttr: '@obj'</code>

<a href="http://s3.51cto.com/wyfs02/M00/6F/34/wKioL1WU0h-Dwg-tAAA0WDfMl9g890.jpg" target="_blank"></a>

   值得一提的是,@bindingStratry是把目前屬性作為一個字元串傳遞,是以對象等引用類型傳過來會變成字元串,最好還是傳字元串類型的資料過來。

本文轉自 iampomelo 51CTO部落格,原文連結:http://blog.51cto.com/iampomelo/1669747,如需轉載請自行聯系原作者

繼續閱讀