前面提到了angularjs的factory,service,provider,這個能夠理解成php的model,這種model是不帶html的,今天所說的directive,也能夠理解成php的model,也能夠理解成插件,只不過這種model是帶html的,例如:php的分頁函數。express
一,angularjs directive的經常使用格式,以及參數說明數組
1,直接returnapp
- var phonecatDirectives = angular.module('phonecatDirectives', []);
- phonecatDirectives.directive('directiveName', function($inject) {
- return {
- template: '<div></div>',
- replace: false,
- transclude: true,
- restrict: 'E',
- scope: { ... },
- controller: function($scope, $element){ .... },
- compile: function(tElement, tAttrs, transclude) {
- return {
- pre: function preLink(scope, iElement, iAttrs, controller) { ... },
- post: function postLink(scope, iElement, iAttrs, controller) { ... }
- }
- },
- link: function(scope, iElement, iAttrs) { ... }
- };
- });
2,定義一個js的域異步
- var phonecatDirectives = angular.module('phonecatDirectives', []);
- phonecatDirectives.directive('directiveName', function($inject) {
- var mydi = {
- template: '<div></div>',
- ..................... ,
- link: function(scope, iElement, iAttrs) { ... }
- };
- return mydi;
- });
3,angularjs directive 對像參數說明ide
name - 當前scope的名稱,註冊時可使用默認值(不填)。函數
priority(優先級)- 當有多個directive定義在同一個DOM元素時,有時須要明確它們的執行順序。這屬性用於在directive的compile function調用以前進行排序。若是優先級相同,則執行順序是不肯定的(經初步試驗,優先級高的先執行,同級時按照相似棧的「後綁定先執行」。另外,測試時有點不當心,在定義directive的時候,兩次定義了一個相同名稱的directive,但執行結果發現,兩個compile或者link function都會執行)。
terminal(最後一組)- 若是設置爲」true」,則表示當前的priority將會成爲最後一組執行的directive。任何directive與當前的優先級相同的話,他們依然會執行,但順序是不肯定的(雖然順序不肯定,但基本上與priority的順序一致。當前優先級執行完畢後,更低優先級的將不會再執行)。
scope - 若是設置爲:
true - 將爲這個directive建立一個新的scope。若是在同一個元素中有多個directive須要新的scope的話,它仍是隻會建立一個scope。新的做用域規則不適用於根模版(root of the template),所以根模版每每會得到一個新的scope。
{}(object hash) - 將建立一個新的、獨立(isolate)的scope。」isolate」 scope與通常的scope的區別在於它不是經過原型繼承於父scope的。這對於建立可複用的組件是頗有幫助的,能夠有效防止讀取或者修改父級scope的數據。這個獨立的scope會建立一個擁有一組來源於父scope的本地scope屬性(local scope properties)的object hash。這些local properties對於爲模版建立值的別名頗有幫助(useful for aliasing values for templates -_-!)。本地的定義是對其來源的一組本地scope property的hash映射(Locals definition is a hash of local scope property to its source #&)$&@#)($&@#_):
@或@attr - 創建一個local scope property到DOM屬性的綁定。由於屬性值老是String類型,因此這個值老是返回一個字符串。若是沒有經過@attr指定屬性名稱,那麼本地名稱將與DOM屬性的名稱一直。例如<widget my-attr=」hello {{name}}」>,widget的scope定義爲:{localName:’@myAttr’}。那麼,widget scope property的localName會映射出」hello {{name}}"轉換後的真實值。name屬性值改變後,widget scope的localName屬性也會相應地改變(僅僅單向,與下面的」=」不一樣)。name屬性是在父scope讀取的(不是組件scope)
=或=expression(這裏也許是attr) - 在本地scope屬性與parent scope屬性之間設置雙向的綁定。若是沒有指定attr名稱,那麼本地名稱將與屬性名稱一致。例如<widget my-attr=」parentModel」>,widget定義的scope爲:{localModel:’=myAttr’},那麼widget scope property 「localName」將會映射父scope的「parentModel」。若是parentModel發生任何改變,localModel也會發生改變,反之亦然。(雙向綁定)
&或&attr - 提供一個在父scope上下文中執行一個表達式的途徑。若是沒有指定attr的名稱,那麼local name將與屬性名稱一致。例如<widget my-attr=」count = count + value」>,widget的scope定義爲:{localFn:’increment()’},那麼isolate scope property 「localFn」會指向一個包裹着increment()表達式的function。通常來講,咱們但願經過一個表達式,將數據從isolate scope傳到parent scope中。這能夠經過傳送一個本地變量鍵值的映射到表達式的wrapper函數中來完成。例如,若是表達式是increment(amount),那麼咱們能夠經過localFn({amount:22})的方式調用localFn以指定amount的值(上面的例子真的沒看懂,&跑哪去了?)。
controller - controller 構造函數。controller會在pre-linking步驟以前進行初始化,並容許其餘directive經過指定名稱的require進行共享(看下面的require屬性)。這將容許directive之間相互溝通,加強相互之間的行爲。controller默認注入瞭如下本地對象:
$scope - 與當前元素結合的scope
$element - 當前的元素
$attrs - 當前元素的屬性對象
$transclude - 一個預先綁定到當前轉置scope的轉置linking function :function(cloneLinkingFn)。(A transclude linking function pre-bound to the correct transclusion scope)
require - 請求另外的controller,傳入當前directive的linking function中。require須要傳入一個directive controller的名稱。若是找不到這個名稱對應的controller,那麼將會拋出一個error。名稱能夠加入如下前綴:
? - 不要拋出異常。這使這個依賴變爲一個可選項。
^ - 容許查找父元素的controller
restrict - EACM的子集的字符串,它限制directive爲指定的聲明方式。若是省略的話,directive將僅僅容許經過屬性聲明:
E - 元素名稱: <my-directive></my-directive>
A - 屬性名: <div my-directive=」exp」></div>
C - class名: <div class=」my-directive:exp;」></div>
M - 註釋 : <!-- directive: my-directive exp -->
template - 若是replace 爲true,則將模版內容替換當前的HTML元素,並將原來元素的屬性、class一併遷移;若是爲false,則將模版元素看成當前元素的子元素處理。想了解更多的話,請查看「Creating Widgets」章節(在哪啊。。。Creating Components就有。。。)
templateUrl - 與template基本一致,但模版經過指定的url進行加載。由於模版加載是異步的,因此compilation、linking都會暫停,等待加載完畢後再執行。
replace - 若是設置爲true,那麼模版將會替換當前元素,而不是做爲子元素添加到當前元素中。(注:爲true時,模版必須有一個根節點)
transclude - 編譯元素的內容,使它可以被directive所用。須要(在模版中)配合ngTransclude使用(引用)。transclusion的優勢是linking function可以獲得一個預先與當前scope綁定的transclusion function。通常地,創建一個widget,建立isolate scope,transclusion不是子級的,而是isolate scope的兄弟。這將使得widget擁有私有的狀態,transclusion會被綁定到父級(pre-isolate)scope中。(上面那段話沒看懂。但實際實驗中,若是經過<any my-directive>{{name}}</any my-directive>調用myDirective,而transclude設置爲true或者字符串且template中包含<sometag ng-transclude>的時候,將會將{{name}}的編譯結果插入到sometag的內容中。若是any的內容沒有被標籤包裹,那麼結果sometag中將會多了一個span。若是原本有其餘東西包裹的話,將維持原狀。但若是transclude設置爲’element’的話,any的總體內容會出如今sometag中,且被p包裹)
true - 轉換這個directive的內容。(這個感受上,是直接將內容編譯後搬入指定地方)
‘element’ - 轉換整個元素,包括其餘優先級較低的directive。(像將總體內容編譯後,看成一個總體(外面再包裹p),插入到指定地方)
compile - 這裏是compile function,將在下面實例詳細講解
link - 這裏是link function ,將在下面實例詳細講解。這個屬性僅僅是在compile屬性沒有定義的狀況下使用。
三,angularjs directive 實例講解
下面的實例都圍繞着,上面的參數來展開的
1,directive聲明方式實例
- //directive文件directives.js中定義一個myTest
- 'use strict';
- var phonecatDirectives = angular.module('phonecatDirectives', []);
- phonecatDirectives.directive('myTest', function() {
- return {
- restrict: 'ACEM',
- require: '^ngModel',
- scope: {
- ngModel: '='
- },
- template: '<div><h4>Weather for {{ngModel}}</h4></div>'
- }
- });
- //controller文件controller.js中定義directive1
- 'use strict';
- var dtControllers = angular.module('dtControllers', []);
- dtControllers.controller('directive1',['$scope',
- function($scope) {
- $scope.name = 'this is tank test';
- }
- ]);
- //在app文件app.js中整合controller,directive
- 'use strict';
- var phonecatApp = angular.module('phonecatApp', [
- 'phonecatDirectives',
- 'dtControllers'
- ]);
- //html文件
- <script src="../lib/angular/angular.js"></script>
- <script src="../js/app.js"></script>
- <script src="../js/controller.js"></script>
- <script src="../js/directives.js"></script>
- <body ng-app="phonecatApp">
- <div ng-controller="directive1">
- <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>
- <!-- directive: my-test exp -->
- <span ng-model="city"></span>
- </div>
- </body>
上例結果:<!-- directive: my-test exp -->這個不起做用,不知道爲何,嘗試了好多方法都不起做用。
2,template和templateUrl區別與聯繫
templateUrl其實根template功能是同樣的,只不過templateUrl加載一個html文件,上例中,咱們也能發現問題,template後面根的是html的標籤,若是標籤不少呢,那就比較不爽了。能夠將上例中的,template改一下。
- phonecatDirectives.directive('myTest', function() {
- return {
- restrict: 'ACEM',
- require: '^ngModel',
- scope: {
- ngModel: '='
- },
- templateUrl:'../partials/tem1.html' //tem1.html中的內容就是上例中template的內容。
- }
- });
3,scope重定義
- //directives.js中定義myAttr
- phonecatDirectives.directive('myAttr', function() {
- return {
- restrict: 'E',
- scope: {
- customerInfo: '=info'
- },
- template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
- 'Name: {{vojta.name}} Address: {{vojta.address}}'
- };
- });
- //controller.js中定義attrtest
- dtControllers.controller('attrtest',['$scope',
- function($scope) {
- $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
- $scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };
- }
- ]);
- //html中
- <body ng-app="phonecatApp">
- <div ng-controller="attrtest">
- <my-attr info="naomi"></my-attr>
- </div>
- </body>
運行結果:
- Name: Naomi Address: 1600 Amphitheatre //有值,由於customerInfo定義過的
- Name: Address: //沒值 ,由於scope重定義後,vojta是沒有定義的
可能把上面的directive簡單改一下,
- phonecatDirectives.directive('myAttr', function() {
- return {
- restrict: 'E',
- template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
- 'Name: {{vojta.name}} Address: {{vojta.address}}'
- };
- });
運行結果以下:根上面正好相反
- Name: Address:
- Name: Vojta Address: 3456 Somewhere Else
4,transclude的使用
transclude的用法,有點像jquery裏面的$().html()功能
- //directives.js增長myEvent
- phonecatDirectives.directive('myEvent', function() {
- return {
- restrict: 'E',
- transclude: true,
- scope: {
- 'close': '&onClick' //根html中的on-click="hideDialog()"有關聯關係
- },
- templateUrl: '../partials/event_part.html'
- };
- });
- //controller.js增長eventtest
- dtControllers.controller('eventtest',['$scope','$timeout',
- function($scope, $timeout) {
- $scope.name = 'Tobias';
- $scope.hideDialog = function () {
- $scope.dialogIsHidden = true;
- $timeout(function () {
- $scope.dialogIsHidden = false;
- }, 2000);
- };
- }
- ]);
- //event.html
- <body ng-app="phonecatApp">
- <div ng-controller="eventtest">
- <my-event ng-hide="dialogIsHidden" on-click="hideDialog()">
- Check out the contents, {{name}}!
- </my-event>
- </div>
- </body>
- //event_part.html
- <div>
- <a href ng-click="close()">×</a>
- <div ng-transclude></div>
- </div>
5,controller,link,compile有什麼不一樣
- //directives.js增長exampleDirective
- phonecatDirectives.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 ";
- }
- };
- }
- }
- });
- //controller.js添加
- dtControllers.controller('directive2',['$scope',
- function($scope) {
- $scope.number = '1111 ';
- }
- ]);
- //html
- <body ng-app="phonecatApp">
- <div ng-controller="directive2">
- <example-directive></example-directive>
- </div>
- </body>
運行結果:
- Hello 1111 22222 44444 55555 !
由結果能夠看出來,controller先運行,compile後運行,link不運行。
將上例中的compile註釋掉
- // 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 ";
- // }
- // };
- // }
運行結果:
- Hello 1111 22222 33333 !
由結果能夠看出來,controller先運行,link後運行,link和compile不兼容。
scope:{}
使指令與外界隔離開來,使其模板(template)處於non-inheriting(無繼承)的狀態,固然除非你在其中使用了transclude嵌入,這點以後的筆記會再詳細記錄的。可是這顯然不符合實際開發中的需求,由於實際上,咱們常常想要咱們的指令可以在特定的狀況下與外界進行數據上的交互,這就須要藉助綁定策略之手了。
你們知道,當scope選項寫爲scope:{}這種形式的時候,就已經爲指令生成了隔離做用域,如今,咱們來看看綁定策略的
三種形式:
& 綁定方法,將傳入的方法名在指令裏註冊爲方法
= 綁定對象.雙向綁定
@ 綁定字符串. 設置Title之類的初始化字符. 單向綁定
首先是@,它將本地做用域和DOM中的屬性值綁定起來(且這個屬性的值必須是父級做用域中的),什麼意思呢?說的簡單一點就是假設你在模板中有個雙花括號表達式,而後咱們把表達式裏的內容和html中指令裏特定名字的屬性綁定起來,仍是不懂?看看下面的代碼:
JS代碼:
directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:{{ name }}</div>',
scope:{
name:'@forName'
}
}
})
.controller("nameController",function($scope){
$scope.Name="張三";
});
HTML代碼:
<div ng-controller="nameController">
<direct for-name="{{ Name }}"></direct>
<div>
運行結果可想而知,{{ name }}成功地與父控制器中的Name綁定起來了。固然這裏也能夠這樣寫
name:'@' 這樣寫的話,就默認DOM中的屬性名爲name了意即 for-name="{{ Name }}"可簡寫爲name="{{ Name }}";其實,另外兩個符號=和&也有這樣的簡寫規則,方便起見接下來都使用這種寫法。
@到此爲止,接下來就是'='了。=與@的不一樣點在於,@是針對字符串而用,但=是針對某個對象的引用,
這麼說可能不太專業,但就拿上邊的例子而言,咱們在html中,把Name這個字符串經過一對雙花括號傳遞給for-name屬性,但若是咱們用了=,這裏傳入的Name就不該該是一個字符串,而是一個對象的引用。這不是一個很一目瞭然的概念,因此我用接下來的兩個例子詮釋它的含義。
第一個例子:數組中的對象的引用
JS代碼:
directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:{{ case.name }}</div>',
scope:{
case:'='
}
}
})
.controller("nameController",function($scope){
$scope.data=[{name:"張三"},{name:"李四"}];
});
HTML代碼:
<div ng-controller="nameController">
<direct case="data[0]"></direct>
<direct case="data[1]"></direct>
<div>
結果就是,一個張三,一個李四。這個例子中,data是一個對象數組,裏面包含了兩個對象,因此,咱們分別把兩個對象傳遞給了case這個屬性,case屬性就把這個對象的引用傳遞給了模板中咱們寫的{{ case.name }}中的case;而若是你在=後邊加上了本身定義的名字,那隻要把html裏case屬性換成那個名字就能夠了。
第二個例子:經典的雙向輸入框
按照Angular的入門案例,建立兩個雙向綁定的輸入框,最簡單的實現方式就是:
<input ng-model="test"/>
<input ng-model="test"/>
使用ng-model指令就能夠作到了。接着,咱們在本身的指令中實現這個效果。
JS代碼:
directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:<input ng-model="model"/></div>',
scope:{
model:'='
}
}
})
.controller("nameController",function($scope){
$scope.data=[{name:"張三"},{name:"李四"}];
});
HTML代碼:
<div ng-controller="nameController">
父級scope中:<input ng-model="mark"/>
<direct model="mark"/></direct>
</div>
這就完成了,其實只不過是加了一點小把戲,把ng-model換成了model而已。
注意到,這兩個例子中,都是使用對象的引用,而不是單純的字符串,這也是=能夠進行雙向綁定的關鍵。
最後是&符號。它的含義是:對父級做用域進行綁定,並將其中的屬性包裝成一個函數,注意,是屬性,意即,任何類型的屬性都會被包裝成一個函數,好比一個單純的字符串,或是一個對象數組,或是一個函數方法,若是是字符串、對象數組和無參的函數,那麼可想而知,它們都會被包裝成一個無參的函數,如果有參的函數方法則反之,而且咱們須要爲其傳入一個對象。如今,分別針對有參和無參兩種狀況舉例。
無參狀況↓
JS代碼:
.directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>{{ title }}</div>'+'<div><ul><li ng-repeat="x in contents">{{ x.text }}< /li></ul></div>',
scope:{
getTitle:'&',
getContent:'&'
},
controller:function($scope){
$scope.title=$scope.getTitle(); //調用無參函數
$scope.contents=$scope.getContent(); //調用無參函數
}
}
})
.controller("nameController",function($scope){
$scope.title="標題";
$scope.contents =[{text:1234},{text:5678}];
});
HTML代碼:
<div ng-controller="nameController">
<direct get-title="title" get-content="contents"></direct>
</div>
這個例子有幾個注意點:
1.指令的本地屬性(即模板裏花括號中的屬性)須要從本地取值,因此使用了controller選項,而在controller選項中,兩個無參方法分別返回了父級scope中的title字符串和contents對象數組。
2.在HTML中,咱們把設置了get-title和get-content的屬性值爲title和contents,這實際上就完成了與父級scope的綁定,由於咱們才能夠從那兒取得實質的內容。
OK,有參狀況↓
JS代碼:
.directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div><input ng-model="model"/></div>'+'<div><button ng-click="show({name:model})">show</button>',
scope:{
show:'&'
}
}
})
.controller("nameController",function($scope){
$scope.showName=function(name){
alert(name);
}
});
HTML代碼:
<div ng-controller="nameController">
<direct show="showName(name)"></direct>
</div>
這個例子中,經過模板中的ng-click觸發了show函數並將一個叫作model的對象做爲name參數傳遞了進去,而在html中,咱們把show的屬性值設爲showName(name)。這其中的道理跟無參的例子是大同小異的。
大功告成,@,=,&的綁定策略大概就是這樣了。有什麼須要補充和糾正的,我懇請各位大神向我提出,謝謝!