這一篇從自定義指令出發,記錄了定義一個指令時影響指令行爲的各類因素。javascript
試着感覺這些因素,讓本身更高效地編寫AngularJS應用。html
先從定義一個簡單的指令開始。
定義一個指令本質上是在HTML中經過元素、屬性、類或註釋來添加功能。java
AngularJS的內置指令都是以ng
開頭,若是想自定義指令,建議自定義一個前綴表明本身的命名空間。express
這裏咱們先使用my
做爲前綴:app
var myApp = angular.module('myApp', []) .directive('myDirective', function() { return { restrict: 'A', replace: true, template: '<p>Kavlez</p>' }; })
如此一來,咱們能夠這樣使用,注意命名是camel-case:函數
<my-directive /> <!-- <my-directive><p>Kavlez</p></my-directive> -->
directive()
接受兩個參數spa
應用啓動時,以name做爲該應用的標識註冊factory_function返回的對象。翻譯
在factory_function中,咱們能夠設置一些選項來改變指令的行爲。雙向綁定
下面記錄一下定義指令時用到的選項rest
該屬性用於定義指令以什麼形式被使用,這是一個可選參數,本文開頭定義的指令用的也是A,其實該選項默認爲A。
也就是元素(E)、屬性(A)、類(C)、註釋(M)
(ps:EMAC? EMACS? 挺好記哈)
好比上面定義的myDirective
,能夠以任何形式調用。
<my-directive></my-directive>
<div my-directive="expression"></div>
<div class="my-directive:expression;"></div>
<--directive:my-directive expression-->
也就是優先級,默認爲0。
在同一元素上聲明瞭多個指令時,根據優先級決定哪一個先被調用。
若是priority相同,則按聲明順序調用。
另外,no-repeat
是全部內置指令中優先級最高的。
終端? 並且仍是Boolean?
被名字嚇到了,其實terminal的意思是是否中止當前元素上比該指令優先級低的指令。
可是相同的優先級仍是會執行。
好比,咱們在my-directive
的基礎上再加一個指令:
.directive('momDirective',function($rootScope){ return{ priority:3, terminal:true }; })
調用發現my-directive
不會生效:
<div mom-directive my-directive="content" ></div>
至少得輸出點什麼吧? 但template也是可選的。
String類型時,template能夠是一段HTML。
Function類型時,template是一個接受兩個參數的函數,分別爲:
函數返回一段字符串做爲模板。
這個就和上面的template很像了,只不過此次是經過URL請求一個模板。
String類型時,templateURL天然是一個URL。
Function類型時返回一段字符串做爲模板URL。
默認值爲false,以文章開頭定義的指令爲例,假設咱們這樣調用了指令
<my-directive></my-directive>
replace爲true時,輸出:
<p>Kavlez</p>
replace爲false時,輸出:
<my-directive><p>Kavlez</p></my-directive>
該選項默認爲false,翻譯過來叫'嵌入',感受仍是有些生澀。
template
和scope
已經能夠作不少事情了,但有一點不足。
好比在原有元素的基礎上添加內容,transclude
的例子以下:
<body ng-app="myApp"> <textarea ng-model="content"></textarea> <div my-directive title="Kavlez"> <hr> {{content}} </div> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .directive('myDirective', function() { return { restrict: 'EA', scope: { title: '@', content: '=' }, transclude: true, template: '<h2 class="header">{{ title }}</h2>\ <span class="content" ng-transclude></span>' }; }); </script>
發現div下的hr並無被移除,就是這樣的效果。
注意不要忘了在模板中聲明ng-transclude
。
默認爲false,true時會從父做用域繼承並建立一個本身的做用域。
而ng-controller
的做用也是從父做用域繼承並建立一個新的做用域。
好比這樣,離開了本身的做用域就被打回原形了:
<div ng-init="content='from root'"> {{content}} <div ng-controller="AncestorController"> {{content}} <div ng-controller="ChildController"> {{content}} </div> {{content}} </div> {{content}} </div> .controller('ChildController', function($scope) { $scope.content = 'from child'; }) .controller('AncestorController', function($scope) { $scope.content = 'from ancestor'; })
但不要誤解,指令嵌套並不必定會改變它的做用域。
既然true
時會從父做用域繼承並建立一個本身的做用域,那麼咱們來試試改成false
會是什麼樣子:
<div ng-init="myProperty='test'"> {{ myProperty }} <div my-directive ng-init="myProperty = 'by my-directive'"> {{ myProperty }} </div> {{ myProperty }} </div> .directive('myDirective', function($rootScope) { return { scope:false }; })
顯然,結果是三行'by my-directive'。
非true即false? naive!
其實最麻煩的仍是隔離做用域,
咱們稍微改動一下myDirective,改成輸出<p>{{內容}}</p>
。
因而我試着這樣定義:
<body ng-app="myApp" > <p ng-controller="myController"> <div my-directive="I have to leave." ></div> {{myDirective}} </p> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .directive('myDirective', function($rootScope) { $rootScope.myDirective = 'from rootScope'; return { priority:1000, restrict: 'A', replace: true, scope: { myDirective: '@', }, template: '<p>{{myDirective}}</p>' }; }) .controller('myController',function($scope){ $scope.myDirective = 'from controller'; }); </script>
這裏須要注意的不是@
,重點是隔離做用域。
根據上面的例子輸出,template中的{{myDirective}}
不會影響到其餘做用域。
咱們再試試這樣:
<input type="text" ng-model="content"> <p ng-controller="myController" > <div my-directive="{{content}}" ></div> {{content}} </p>
發現你們都在一塊兒變,也就是說值是經過複製DOM屬性並傳遞到隔離做用域。
ng-model
是個強大的指令,它將本身的隔離做用域和DOM做用域連在一塊兒,這樣就是一個雙向數據綁定。
如何向指令的隔離做用域中傳遞數據,這裏用了@
。
或者也能夠寫成@myDirective
,也就是說換個名字什麼的也能夠,好比我用@myCafe
什麼的給myDirective賦值也是沒問題的,總之是和DOM屬性進行綁定。
另外,咱們也能夠用=
進行雙向綁定,將本地做用域的屬性同父級做用域的屬性進行雙向綁定。
好比下面的例子中,隔離做用域裏的內容只能是'abc' :
<body ng-app="myApp" ng-init="content='abc'"> <p ng-controller="myController" > <input type="text" ng-model="content"> <div my-directive="content" ></div> {{content}} </p> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .directive('myDirective', function($rootScope) { return { priority:1000, restrict: 'A', replace: true, scope: { myDirective: '=', }, template: '<p>from myDirective:{{myDirective}}</p>' }; }) .controller('myController',function($scope){ $scope.content = 'from controller'; }); </script>
在隔離做用域訪問指令外部的做用域的方法還有一種,就是&
。
咱們可使用&
與父級做用域的函數進行綁定,好比下面的例子:
<body ng-app="myApp"> <div ng-controller="myController"> <table border='1'> <tr> <td>From</td> <td><input type="text" ng-model="from"/></td> </tr> <tr> <td>To</td> <td><input type="text" ng-model="to"/></td> </tr> <tr> <td>Content</td> <td><textarea cols="30" rows="10" ng-model="content"></textarea></td> </tr> <tr> <td>Preview:</td> <td><div scope-example to="to" on-send="sendMail(content)" from="from" /></td> </tr> </table> </div> </div> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .controller('myController',function($scope){ $scope.sendMail=function(content){ console.log('content is:::'+content); } }) .directive('scopeExample',function(){ return{ restrict:'EA', scope: { to: '=', from: '=' , send: '&onSend' }, template:'<div>From:{{from}}<br>\ To:{{to}}<br>\ <button ng-click="send()">Send</button>\ </div>' } }) </script>
控制器也能夠在指令裏定義,好比:
.directive('myDirective', function() { restrict: 'A', controller: 'myController' }).controller('myController', function($scope, $element, $attrs,$transclude) { //... })
相同的效果,也能夠這樣聲明:
directive('myDirective', function() { restrict: 'A', controller:function($scope, $element, $attrs, $transclude) { //... } });
能夠從名字和類型看出,這個選項是用來設置控制器的別名的。
好比這樣:
directive('myDirective', function() { restrict: 'A', controller:function($scope, $element, $attrs, $transclude) { //... } });
雖然說這個東西不是很經常使用吧,但倒是值得了解的選項。
compile
和link
,這兩個選項關係到AngularJS的生命週期。
先在這裏簡單記錄一下我對生命週期的認識。
ng-repeat
之類的指令對DOM進行操做則再合適不過了。好了,接下來咱們就試試compile:
<body ng-app="myApp"> <my-directive ng-model="myName"></my-directive> </body> <script type="text/javascript"> var myApp = angular.module('myApp', []) .directive('myDirective', function($rootScope) { $rootScope.myName = 'Kavlez'; return { restrict: 'EA', compile:function(tEle, tAttrs, transcludeFn) { var h2 = angular.element('<h2></h2>'); h2.attr('type', tAttrs.type); h2.attr('ng-model', tAttrs.ngModel); h2.html("hello {{"+tAttrs.ngModel+"}}"); tEle.replaceWith(h2); } }; }); </script>
原文出處 AngularJS - 自定義指令