點擊查看AngularJS系列目錄
轉載請注明出處:http://www.cnblogs.com/leosx/
什么是指令
注:本指南是針對已經熟悉AngularJS基礎知識的開發人員。如果你才剛剛開始,我建議查看系列教程。(這章是Angular的重點)
指令是一個Dom元素上的標簽(和元素上的屬性, CSS 類樣式一樣,屬於這個Dom元素),它告訴AngualrJS的HTML 編譯器($compile),去附加一個行為到這個Dom元素上去,這個行為可以改變這個Dom元素,或者這個Dom元素的子元素。
AngularJS 有一套自己內置的指令,如:ngBind,ngModel,ngClass等等...你也可以自定義自己的指令。當Angular應用程序起來之后,會先去加載Dom樹,然后去匹配你的指令,然后執行。
HTML 編譯器 -- 對於Angularjs來說,編譯意味着遞歸去給HTML添加一些事件監聽,讓DOM元素和Angualr之間可以進行交互,相互作用。這里使用編譯這個術語的原因是:添加事件監聽這個過程,剛好反應了我們C#,Java等編程語言將源代碼編譯成應用的這個過程。
匹配指令
在我們編寫我們的第一個自定義指令之前,我能需要知道AngularJS的HTML編譯器是怎么確定在什么時候使用我們自定義的指令的。
下面這個例子中,我們可以看到<Input>元素匹配到了 ngModel 這個指令。
<input ng-model="foo">
下面這個例子也是匹配到了 ngModel 這個指令:
<input data-ng:model="foo">
AngularJS使用元素標簽類型(eg:input)和屬性名稱來確定哪個元素匹配到了哪個指令。標准的指令(eg: ngModel)是區分大小寫的,使用了駝峰命名法則。然而,由於HTML是不區分大小寫的,所以后來也不區分大小寫了…(感覺等於沒說…) 但是通常我們都是使用 [-] 這個破折號來代表一個指令(通常,並不是必須)。
一般的使用方法如下:
1.使用 x- 或者 data- 在元素或者屬性前。
2.使用 : 或者 - 或者 _ 來代替了駝峰命名法則。
<div ng-app="docsBindExample"> <div ng-controller="Controller"> Hello <input ng-model='name'> <hr/> <span ng-bind="name"></span> <br/> <span ng:bind="name"></span> <br/> <span ng_bind="name"></span> <br/> <span data-ng-bind="name"></span> <br/> <span x-ng-bind="name"></span> <br/> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsBindExample', []) .controller('Controller', ['$scope', function($scope) { $scope.name = 'Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)'; }]); })(); </script>
效果圖:
$compile編譯器會根據元素名稱,屬性,CSS類名,甚至是注釋去匹配指令
下面展示了一個指令可以再哪些地方被引用:
<my-dir></my-dir> <span my-dir="exp"></span> <!-- directive: my-dir exp --> <span class="my-dir: exp;"></span>
建議1: 最好的使用指令方式是通過標簽名,屬性名,而不是使用注釋和CSS樣式類名。這樣做可以讓編譯器更加容易的去正確匹配指令。
建議2: 注釋指令通常用來解決Dom API的限制,進行跨越多個元素的操作。比如在 <Table> 元素內,去跨越多個元素進行操作。Angular1.2中引入了ng-repeat-start和ng-repeat-end指令,它們可以更好的去解決這個問題。鼓勵開發者使用它們,而不是使用注釋指令去解決這樣的問題。
文本和屬性綁定
在編譯過程當中,編譯器會根據文本的屬性值和使用了$interpolate服務的屬性去查看這個HTML元素是否包含嵌入式的表達式。如果有,那么這些表達式會被注冊到監視器中,並且會進入$digest階段。下面是一個使用了$interpolate 表達式的示例:
<a ng-href="img/{{username}}.jpg">Hello {{username}}!</a>
ngAttr屬性綁定
我們的瀏覽器有時候會挑剔什么是他們認為又要的屬性值(也就是屬性值不是你隨便寫的) 想想看下面的例子:
<svg> <circle cx="{{cx}}"></circle> </svg>
我們很希望Angular允許我們這樣綁定,但是當我們打開瀏覽器控制台的時候,你就會看到如下錯誤:
Error: Invalid value for attribute cx="{{cx}}".
這是因為SVG DOM API的限制原因,你就不能寫成: cx=”{{cs}}”了,而應該使用: ng-attr-cx 來解決這個問題。
如果一個屬性的綁定的前綴是 ngAttr 而不是正規的 ng-attr-這種形式,那么在binding期間,它會被應用到相應的沒有前綴的屬性上去(eg: <circle ngAttr-cx=”a”></circle> 它是不會被應用到cx屬性上去的)。因為當使用ngAttr前綴的時候,對於$interpolate階段來說,任何標識都還不起作用。所以在這個階段中所有的string結果都是 undefined的,既然是undefined的,那么對應的這個屬性會被移除,而不再繼續添加到元素上去。
我們可以如下去解決上面提到的問題:
<svg> <circle ng-attr-cx="{{cx}}"></circle> </svg>
注冊指令
我們先來談談和注冊指令相關的API,和controller一樣,指令也是被注冊到模塊(modules)上的,創建指令的API是: module.directive(指令名稱,功能函數)。module.directive(direName,func) ,這個func方法需要返回一個 配置參數對象,通常是一個Json對象,來告訴$compile編譯器,進行這個指令匹配時的行為及動作.
這個func功能函數只會在當編譯器第一次匹配到指令的時候執行一次。你可以在這里執行任何的初始化工作。這個func函數是被 $injector.invoke方法調用的。
友情提示:通常,為了避免與未來一些指令沖突,我們自定義的時候,都最好有一個自己的前綴。。。
模板擴展指令
我們想想,如果你現在需要一個模板,去展示客戶的信息,而且這個模板會重復出現很多次,當你要改變一個地方,你就得去改很多次。這個是一個很好的情景讓你去使用指令(directive)去簡化你的模板(你該使用模板擴展指令了)。
下面,我們就是用一個例子來說明:
<div ng-app="docsSimpleDirective"> <div ng-controller="Controller"> <div my-customer></div> </div> <div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsSimpleDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.customer = { name: 'Naomi', address: '1600 Amphitheatre' }; }]) .directive('myCustomer', function() { return { template: 'Name: {{customer.name}} Address: {{customer.address}}' }; }); })(); </script>
效果圖:
當$compile編譯器編譯,並且連接了<div my-customer></div>之后,它將會繼續嘗試匹配這個元素的子元素。這就意味着你可以篡改其它指令哦!一會兒我們來看下面的例子。
還有需要注意的,上面的例子當中,我們返回了參數 template,當模板大小改變的時候,這是很煩人的哦!你的字符串會很長,很長! 哈哈!好像邪惡了。 除非你的模板就像上面例子一樣,確實特別小,否則,我們還是建議你把模板嚇到一個單獨的HTML文件當中去,然后再指令的func中,我們返回的參數中,使用templateUrl選項,來指定我們的模板路徑。
<div> <div ng-controller="Controller"> <div my-customer></div> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsTemplateUrlDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.customer = { name: 'Naomi', address: '1600 Amphitheatre' }; }]) .directive('myCustomer', function() { return { templateUrl: 'my-customer.html' }; }); })(); </script>
my-customer.html的內容:
Name: {{customer.name}} Address: {{customer.address}}
效果圖:
其實,templateUrl選項也可以是一個函數,返回要加載的HTML模板的URL路徑,Angular會自動調用templateUrl函數,並且傳入兩個參數:ele(對應的元素), 和一個與這個元素相關的attr對象。
注意:在scope沒有初始化之前,你是不能夠在templateUrl函數中訪問scope的,所以說,Controller的初始化一定是要在使用templateUrl函數之前。
<div ng-app="docsTemplateUrlDirective"> <div ng-controller="Controller"> <div my-customer type="name"></div> <div my-customer type="address"></div> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsTemplateUrlDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.customer = { name: 'Naomi', address: '1600 Amphitheatre' }; }]) .directive('myCustomer', function() { return { templateUrl: function(elem, attr){ return 'customer-'+attr.type+'.html'; } }; }); })(); </script>
customer-name.html
Name: {{customer.name}}
customer-address.html
Address: {{customer.address}}
效果圖:
再次注意:默認情況下,你創建的指令僅限於用於屬性或者元素,為了創建一個可以用於類(class)的觸發器,你需要使用到restrict選項。
restrict選項的幾個經典選項:
‘A’ - 僅僅匹配屬性名
‘E’ - 僅僅去匹配元素名
‘C’ - 僅僅匹配class名
友情提示:這些選項可以根據需要進行組合的哦!!
‘AEC’- 它可以匹配屬性(attr)或者元素(ele)或者類名(class)。
我們來寫一個例子,讓我們的這個指令只能去匹配元素名,也就是說這個指令只能用於元素上。
<div ng-app="docsRestrictDirective"> <div ng-controller="Controller"> <my-customer></my-customer> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsRestrictDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.customer = { name: 'Naomi', address: '1600 Amphitheatre' }; }]) .directive('myCustomer', function() { return { restrict: 'E', // 注意這里哦! templateUrl: 'my-customer.html' }; }); })(); </script>
my-customer.html內容如下:
Name: {{customer.name}} Address: {{customer.address}}
效果圖:
更多的關於restrict選項,你可以參考API文檔(你知道的,有可能訪問不了哦!腫么破,這里不說哈!)
更大的問題來了。什么時候我該用屬性,什么時候我們該用元素呢? 不出意外,其實你應該能知道,什么時候該用什么。 當我們要創建一個控件的時候,那我們就得使用元素匹配方式。當你需要給你現在的元素增加一些新功能,進行修飾,擴展的時候,我們就得使用屬性了。不多解釋…
指令獨立的scope
大家可以想一想,上面說的關於指令的使用的例子,就會發現它有個致命的缺陷,一個Scope(也就是一個Controller)里面你只能使用一次我們自定義的myCustomer指令。這樣的話,我們如果想要重復的使用這個指令的時候,你就得聲明與之對應個數的Controller,Oh~~ my god !!! 你肯定瞬間不想使用Angular了,不錯不要着急,會有解決方法的。
我們可以使用給每個directive(指令)一個獨立的scope來進行分離,然后將外部Controller中的scope映射到directive(指令)內部的獨立的scope上去。這樣,我們就可以調用自己獨立的scope啦! 是不是很好哦!要實現上面的獨立,AngularJS給我們提供了一個directive的scope參數,看下面示例,你就明白了!
<div ng-app="docsIsolateScopeDirective"> <div ng-controller="Controller"> <my-customer info="naomi"></my-customer> <hr> <my-customer info="igor"></my-customer> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsIsolateScopeDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' }; $scope.igor = { name: 'Igor', address: '123 Somewhere' }; }]) .directive('myCustomer', function() { return { restrict: 'E', scope: { customerInfo: '=info' }, templateUrl: 'my-customer-iso.html' }; }); })(); </script>
my-customer-iso.html內容如下:
Name: {{customerInfo.name}} Address: {{customerInfo.address}}
效果圖:
我們來解釋一下上面,在<my-customer>元素中,我們給它的info屬性中,綁定了Controller中的Scope中的naomi屬性上去了。 第二個<my-customer>元素的info屬性我們是給綁定了igor屬性。
我們來仔細看看scope選項:
// ...
scope:{
customerInfo: '=info'
},
// ...
scope選項中,包涵了所有關於這個指令獨立的scope信息,在上面例子中,它只包涵了一個屬性:
customerInfo 是這個指令獨立的scope的屬性名,它的值為 =info ,這是在告訴$compile編譯器,去把這個屬性綁定到一個叫做info的屬性上去。。。
注意:上面的綁定是一個很標准的綁定方式,但是如果你想綁定成這樣:<div bind-to-this=”thing”>,你需要將customerInfo的值設置為 =bindToThis (也就是駝峰命名法則)
我們來考慮還有一種情況,如果說你想就讓綁定的屬性名和你這個指令獨立的scope的屬性名一樣的話,你可以使用下面這個簡寫語法:
...
scope: {
// same as customer: '=customer'
customer: '='
},
...
除了可以將不同的數據綁定到directive 獨立的scope上之外,使用獨立的scope還有另外一個效果.
不解釋,先來段代碼:
<div ng-app='docsIsolationExample'> <div ng-controller="Controller"> <my-customer info="naomi"></my-customer> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsIsolationExample', []) .controller('Controller', ['$scope', function($scope) { $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' }; $scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' }; }]) .directive('myCustomer', function() { return { restrict: 'E', scope: { customerInfo: '=info' }, templateUrl: 'my-customer-plus-vojta.html' }; }); })(); </script>
my-customer-plus-vojta.html 內容如下:
Name: {{customerInfo.name}} Address: {{customerInfo.address}} <hr> Name: {{vojta.name}} Address: {{vojta.address}}
效果圖如下:
請注意: {{vojta.name}} 和 {{vojta.address}} 值是空的。也就是他們根本沒有被定義。雖然我們在controller中的scope中定義了vojta屬性,但是,它在我們自定義的directive(指令)中,是不可用的。。。
顧名思義,在這個directive的獨立scope中,除了實體模型你需要明確的增加之外,你可以添加任何的東西到這個獨立的scope中,那么這樣的話,我們就可以在這里進行重構組件,這樣的話,我們就可以防止控件改變了你的模型的狀態,當然,除了你明確的指定將這個模型傳入進來了,你都傳進來了,就可以改變了。
注意:在一般情況下,一個scope是從上級的scope派生來的,但是這里是一個特例哦! directive(指令)中的scope不是那樣的,如果你想要了解更多的關於定義指令的對象- scope 你可以看看這里。
建議: 當我們想要讓我們自定義的組件在你的整個Angular應用程序中重用的話,你就要使用獨立的scope,但是,如果你只使用一次的話,就可以不用了。不過個人建議,最好還是使用吧! *^_^*
創建一個指令,去操作DOM
在這個例子當中。我們去創建一個指令,用於顯示當前的時間。一秒更新一次,我們就需要更新DOM去顯示當前時間。
通常,我們如果想要在指令中去修改DOM元素的話(Angular不建議你直接去操作DOM),會使用 link 選項 ,link 選項的簽名如下:
function link(scope, element, attrs){ ...... }
[ scope ]是一個Angular的scope對象
[ element ]是這個指令所匹配到的那個元素。
[ attrs ] 是一個規范的key-value鍵值對兒的hash對象
在link 方法中,我們想每秒鍾更新一下顯示時間, 可以使用Angular自帶的 $interval 服務,相比之下,它要比 $timeout 服務更加好用,而且更加的易於端對端測試,它會保證在你的測試完成之前,你的$timeout 里面的所有執行都已完成,而且,它也可以保證當你把directive(指令)移除了之后,會自動的把 $interval 服務給卸載掉,從而不會造成內存的泄露。
是騾子是馬,咱們還是直接拉出來溜溜!! 直接上代碼!
<div ng-ap="docsTimeDirective"> <div ng-controller="Controller"> Date format: <input ng-model="format"> <hr/> Current time is: <span my-current-time="format"></span> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsTimeDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.format = 'M/d/yy h:mm:ss a'; }]) .directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) { function link(scope, element, attrs) { var format, timeoutId; function updateTime() { element.text(dateFilter(new Date(), format)); } scope.$watch(attrs.myCurrentTime, function(value) { format = value; updateTime(); }); element.on('$destroy', function() { $interval.cancel(timeoutId); }); // start the UI update process; save the timeoutId for canceling timeoutId = $interval(function() { updateTime(); // update DOM }, 1000); } return { link: link }; }]); })(); </script>
看看效果圖(自己去實踐哈,這里不做jif的動態圖了哈):
這里有幾個需要注意的地方。 就像module.contoller API一樣,module.directive函數也是依賴注入的,因此,我們可以注入 $interval 和 dateFilter(過濾器),然后再link 函數中進行使用了。
我們注冊了事件 element.on(‘$destory’, … ) 在 $destory 事件中,發生了什么事情呢?
在AngularJS里有幾個特別的事件。當一個DOM節點在Angular的編譯器中要被銷毀,會觸發 $destory 事件。同樣的,當一個AngularJS的 scope 對象被銷毀的時候,也會廣播一個 $destory 事件,給那些監聽scope的對象。
通過監聽這個事件,你可以去刪除哪些可能會導致內存溢出的監聽器。已注冊監聽的scope和元素(element)會在它們被銷毀的時候自動清理掉,我們是不用去手動銷毀的。但是,如果你你注冊了一個服務,或者一個監聽DOM節點的監聽器,而且它們還被刪除了,那么,你就得自己手動去清理。否則可能會造成內存泄露。
建議:指令應該由他們自己去清理的。也就是說,你需要調用 element.on( ‘$destory’, … ) 或者 scope.$on( ‘$destory’, …) 去運行一個清理函數(func),來保證當這個指令被清除的時候,會清除掉和它相關的資源。
我們來創建一個封裝了其它元素的指令:
現在你知道了怎么傳遞一個model到 directive上的獨立scope上去。 但是,有的時候,我們需要傳遞整個模板,而不只是一個字符串或者一個對象而已,那怎么辦呢? 假設我們現在需要創建一個對話框組件。對話框應該可以顯示任意對象,該怎么實現呢? 這需要用到另外的一個選項:transclude 直接上代碼:
<div ng-app="docsTransclusionDirective"> <div ng-controller="Controller"> <my-dialog>Check out the contents, {{name}}!</my-dialog> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsTransclusionDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.name = 'Tobias'; }]) .directive('myDialog', function() { return { restrict: 'E', transclude: true, templateUrl: 'my-dialog.html' }; }); })(); </script>
my-dialog.html頁面內容:
<div class="alert" ng-transclude> </div>
我們來看看效果圖:
那這個 transclude 選項做了些什么事情呢? transclude 選項會讓指令去訪問來自指令之外的 scope 。而不是去訪問自己內部的scope 。
還是使用例子來解釋下吧! 下列中,請注意,我們添加了一個 link 函數,它重新設置了scope中的name屬性的值為 “Jeff”,你猜猜在 {{name}} 綁定中,會顯示什么呢?
<div ng-app="docsTransclusionExample"> <div ng-controller="Controller"> <my-dialog>Check out the contents, {{name}}!</my-dialog> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsTransclusionExample', []) .controller('Controller', ['$scope', function($scope) { $scope.name = 'Tobias'; }]) .directive('myDialog', function() { return { restrict: 'E', transclude: true, scope: {}, templateUrl: 'my-dialog.html', link: function (scope, element) { scope.name = 'Jeff'; } }; }); })(); </script>
my-dialog.html 中的內容如下:
<div class="alert" ng-transclude> </div>
一般來說,我們期望 {{name}} 里面顯示的是 “Jeff”,但是,but,事實上,它顯示的依然是 “Tobias”。選項 transclude 改變了 scope的嵌套方式,它會讓{{name}} 指令去訪問指令外的scope,而不是去訪問指令內部的scope 。
注意:如果你的指令沒有創建自己的scope,那么你再在link 中, scope.name= ‘Jeff’的話,就會看到輸出的結果是 “Jeff”了,因為這樣相當於重新定義了外部scope的name屬性值為 “Jeff”。不要繞暈了哦!
這個對於那些需要包涵一些不同內容(內容可變)的指令來說非常有意義哦! 沒有這個特性的話,你就得分別創建多個不同的實例了。
建議: 從我們的使用來看就可以知道, transclude選項的使用情景應該是當你的指令內容中,需要包含經常變化或者是不定的地方。
接下來,我們來創建一個有按鈕的對話框,它允許我們去綁定一些我們自定義的行為上去,直接上代碼:
<div ng-app="docsIsoFnBindExample"> <div ng-controller="Controller"> <my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()"> Check out the contents, {{name}}! </my-dialog> </div> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsIsoFnBindExample', []) .controller('Controller', ['$scope', '$timeout', function($scope, $timeout) { $scope.name = 'Tobias'; $scope.hideDialog = function () { $scope.dialogIsHidden = true; $timeout(function () { $scope.dialogIsHidden = false; }, 2000); }; }]) .directive('myDialog', function() { return { restrict: 'E', transclude: true, scope: { 'close': '&onClose' }, templateUrl: 'my-dialog-close.html' }; }); })(); </script>
my-dialog-close.html 內容如下:
<div class="alert"> <a href class="close" ng-click="close()">×</a> <div ng-transclude></div> </div>
再來看看效果圖:
當我們點擊關閉按鈕的時候,它會把下面那句話隱藏掉。兩秒鍾后,再顯示出來,自己親自去試!
正如我們所知道一樣,我們可以在指令的scope中定義一些函數(func),然后在scope的上下文中去調用它,但是注意的是,這個函數並需先定義。
我們之前看到了怎么樣在scope選項中使用 =attr 。但是上面的例子當中,我們使用了 &attr 實例。這里的這個 & 綁定(別看只是一個符號,它是一個綁定哦!) 允許指令在原始scope的特定時間,去觸發調用一個表達式。這個表達式不限制,任意表達式都行,包括那些包涵了方法,函數調用的表達式。正因如此, & 綁定符,是一個很理想的 綁定 指令的回調函數 的方式。
當用戶點擊對話框中的X按鈕的時候,指令的close 函數將被調用,由於ng-click的執行,事實上,它會調用它指令自己獨立的scope,而那個scope選項(是選項哦,不要搞暈了),又去調用了上下文中父級scope的 hideDialog() 方法。因此實現了控制器中的hideDialog功能。
建議: 當你想要給你的自定義指令暴露一個綁定行為的API的時候,你就可以使用scope選項 的 &attr 。
創建一個增加事件監聽的指令
以前,我們使用 link 選項函數去創建一個指令去操作DOM元素。下面,我們來創建一個能夠反映使用它的元素的事件的指令。這個示例中,我們會創建一個可以讓元素拖動的指令。
先上例子:
<div ng-app="dragModule"> <span my-draggable>Drag ME</span> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('dragModule', []) .directive('myDraggable', ['$document', function($document) { return function(scope, element, attr) { var startX = 0, startY = 0, x = 0, y = 0; element.css({ position: 'relative', border: '1px solid red', backgroundColor: 'lightgrey', cursor: 'pointer' }); element.on('mousedown', function(event) { // Prevent default dragging of selected content event.preventDefault(); startX = event.pageX - x; startY = event.pageY - y; $document.on('mousemove', mousemove); $document.on('mouseup', mouseup); }); function mousemove(event) { y = event.pageY - startY; x = event.pageX - startX; element.css({ top: y + 'px', left: x + 'px' }); } function mouseup() { $document.off('mousemove', mousemove); $document.off('mouseup', mouseup); } }; }]); })(); </script>
效果圖:
創建一個用於通信的指令。
還是先上例子吧!
<div ng-app="docsTabsExample"> <my-tabs> <my-pane title="Hello"> <h4>Hello</h4> <p>Lorem ipsum dolor sit amet</p> </my-pane> <my-pane title="World"> <h4>World</h4> <em>Mauris elementum elementum enim at suscipit.</em> <p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p> </my-pane> </my-tabs> </div> <script src="https://code.angularjs.org/1.3.0/angular.min.js"></script> <script type="text/javascript"> (function(){ angular.module('docsTabsExample', []) .directive('myTabs', function() { return { restrict: 'E', transclude: true, scope: {}, controller: function($scope) { var panes = $scope.panes = []; $scope.select = function(pane) { angular.forEach(panes, function(pane) { pane.selected = false; }); pane.selected = true; }; this.addPane = function(pane) { if (panes.length === 0) { $scope.select(pane); } panes.push(pane); }; }, templateUrl: 'my-tabs.html' }; }) .directive('myPane', function() { return { require: '^myTabs', restrict: 'E', transclude: true, scope: { title: '@' }, link: function(scope, element, attrs, tabsCtrl) { tabsCtrl.addPane(scope); }, templateUrl: 'my-pane.html' }; }); })(); </script>
my-tabs.html 內容如下:
<div class="tabbable"> <ul class="nav nav-tabs"> <li ng-repeat="pane in panes" ng-class="{active:pane.selected}"> <a href="" ng-click="select(pane)">{{pane.title}}</a> </li> </ul> <div class="tab-content" ng-transclude></div> </div>
my-pane.html 內容如下:
<div class="tab-pane" ng-show="selected" ng-transclude> </div>
效果圖:
在 myPane 指令中,使用到了一個 require選擇,其值為: ^myTabs 。當一個指令使用了這個選項的, $compile 編譯器會需要讓你提供一個 contorller ,不然的話,它會拋出一個異常的。 使用 ^ 前綴意味着這個指令會在它的父級元素(如果沒有 ^ 前綴的話,它會在自己的元素里面查找Controller)里面去查找Controller。
那么,這個 myTabs controller又是從哪里來的呢? 指令可以使用Controller選項去指定controller從哪里來! 就像上面例子一下, myTabs 指令使用了這個選項。 這個選項的值和AngularJS的控制器( ngController )寫法一樣,這個選項也可以附加一些模板給這個指令。
如果有必要引用一個controller或者任何的函數,然后將他們綁定到模板的controller的scope上去的話,你可以使用 controllerAs 選項來為controller指定一個別名。 你需要給指令定義一個用於使用的scope配置,在指令作為一個組件來使用的時候,這是特別有用的哦!
再來回顧一下myPane 的定義,注意 link函數的最后的一個參數: tabsCtrl。 當一個指令需要一個控制器的時候,在 link 函數中,會傳到第四個參數中去。我們可以在第四次參數中得到。 利用這一點,myPane可以調用myTabs中的addPane功能了。
如果你需要多個控制器的話,可以給 require選項傳入一個數組。如下:
angular.module('docsTabsExample', [])
.directive('myPane', function() {
return {
require: ['^myTabs', '^ngModel'],
restrict: 'E',
transclude: true,
scope: {
title: '@'
},
link: function(scope, element, attrs, controllers) {
var tabsCtrl = controllers[0],
modelCtrl = controllers[1];
tabsCtrl.addPane(scope);
},
templateUrl: 'my-pane.html'
};
});
有些讀者可能很想知道 link 函數和 controller之間的區別。 它們最基本的區別是,controller是一個暴露的API, 而link 函數是讓元素和控制器進行交互的。
建議: 當你確定你需要向外,向其他指令暴露一個API的時候,你就得使用controller,其它的情況,還是使用 link比較好!!!
簡單說明一下,這一章很重要,但是這里呢,我們只是寫了一些指令的用例,但是每個用例都是你去創造更多指令的一個很不錯的起點哦!! 發揮你的想要把!
如果你有興趣了解一下編譯過程的話,你可以點擊這里。
如果你想要查看一下 $compile API 編譯器提供的詳細的指令的話,你可以點擊 這里。








