angular.module("app",[]).directive("directiveName",function(){javascript
return{ //經過設置項來定義 };
})其中return返回的對象包含不少參數,下面一一說明css
你知道用AngularJs怎麼定義指令嗎?html
1.restrictjava
(字符串)可選參數,指明指令在DOM裏面以什麼形式被聲明;angularjs
取值有:E(元素),A(屬性),C(類),M(註釋),其中默認值爲A;express
E(元素):<directiveName></directiveName>
A(屬性):<div directiveName='expression'></div>
C(類): <div class='directiveName'></div>
M(註釋):<--directive:directiveName expression-->數組
2.priority緩存
(數字),可選參數,指明指令的優先級,若在單個DOM上有多個指令,則優先級高的先執行;服務器
設置指令的優先級算是不經常使用的app
比較特殊的的例子是,angularjs內置指令的ng-repeat的優先級爲1000,ng-init的優先級爲450;
3.terminal
(布爾型),可選參數,能夠被設置爲true或false,若設置爲true,則優先級低於此指令的其餘指令則無效,不會被調用(優先級相同的仍是會執行)
4.template(字符串或者函數)可選參數,能夠是:
(1)一段HTML文本
angular.module("app",[]).directive("hello",function(){
return{ restrict:'EA', template:"<div><h3>hello world</h3></div>" }; })
HTML代碼爲:<hello></hello>
結果渲染後的HTML爲:<hello>
<div><h3>hello world</h3></div>
</hello>
(2)一個函數,可接受兩個參數tElement和tAttrs
其中tElement是指使用此指令的元素,而tAttrs則實例的屬性,它是一個由元素上全部的屬性組成的集合(對象)形如:
{
title:‘aaaa’,
name:'leifeng'
}
下面讓咱們看看template是一個函數時候的狀況
angular.module("app",[]).directive("directitle",function(){
return{ restrict:'EAC', template: function(tElement,tAttrs){ var _html = ''; _html += '<div>'+tAttrs.title+'</div>'; return _html; } }; })
HTML代碼:<directitle title='biaoti'></directitle>
渲染以後的HTML:<div>biaoti</div>
由於一段HTML文本,閱讀跟維護起來都是很麻煩的,所用一般會使用templateUrl這個。
5.templateUrl(字符串或者函數),可選參數,能夠是
(1)一個表明HTML文件路徑的字符串
(2)一個函數,可接受兩個參數tElement和tAttrs(大體同上)
注意:在本地開發時候,須要運行一個服務器,否則使用templateUrl會報錯 Cross Origin Request Script(CORS)錯誤
因爲加載html模板是經過異步加載的,若加載大量的模板會拖慢網站的速度,這裏有個技巧,就是先緩存模板
你能夠再你的index頁面加載好的,將下列代碼做爲你頁面的一部分包含在裏面。
<script type='text/ng-template' id='woshimuban.html'>
<div>我是模板內容</div>
</script>這裏的id屬性就是被設置在templateUrl上用的。
另外一種辦法緩存是:
angular.module("template.html", []).run(["$templateCache", function($templateCache) {$templateCache.put("template.html",
"<div>wo shi mu ban</div>");
}]);
6.scope
可選參數,(布爾值或者對象)默認值爲false,可能取值:
(1)默認值false。
表示繼承父做用域;
(2)true
表示繼承父做用域,並建立本身的做用域(子做用域);
(3){}
表示建立一個全新的隔離做用域;
接下來咱們經過一個簡單明瞭的例子來講明scope取值不一樣的差異
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>
<div ng-controller='MainController'>
父親: {{name}} <input ng-model="name" /> <div my-directive></div>
</div>
<script>
1 angular.module('myApp', []) 2 .controller('MainController', function ($scope) { 3 $scope.name = 'leifeng'; 4 }) 5 .directive('myDirective', function () { 6 return { 7 restrict: 'EA', 8 scope:false,//改變此處的取值,看看有什麼不一樣 9 template: '<div>兒子:{{ name }}<input ng-model="name"/></div>' 10 }; 11 });
依次設置scope的值false,true,{},結果發現(你們別偷懶,動手試試哈)
. 當爲false時候,兒子繼承父親的值,改變父親的值,兒子的值也隨之變化,反之亦如此。(繼承不隔離)
. 當爲true時候,兒子繼承父親的值,改變父親的值,兒子的值隨之變化,可是改變兒子的值,父親的值不變。(繼承隔離)
. 當爲{}時候,沒有繼承父親的值,因此兒子的值爲空,改變任何一方的值均不能影響另外一方的值。(不繼承隔離)
tip:當你想要建立一個可重用的組件時隔離做用域是一個很好的選擇,經過隔離做用域咱們確保指令是‘獨立’的,並能夠輕鬆地插入到任何HTML app中,而且這種作法防止了父做用域被污染;
6.2隔離做用域能夠經過綁定策略來訪問父做用域的屬性。
1 <!doctype html> 2 <html ng-app="myApp"> 3 <head> 4 <script src="http://cdn.staticfile.org/ang... 5 </head> 6 <body> 7 8 <div ng-controller='MainController'> 9 10 <input type="text" ng-model="color" placeholder="Enter a color"/> //這裏輸入的color不會被檢測到指令中 11 <hello-world></hello-world> 12 </div> 13 14 <script> 15 16 var app = angular.module('myApp',[]); 17 app.controller('MainController',function(){}); 18 app.directive('helloWorld',function(){ 19 return { 20 scope: {}, 21 restrict: 'AE', 22 replace: true, 23 template: '<p style="background-color:{{color}}">Hello World</p>' 24 } 25 }); 26 </script> 27 28 </body> 29 </html>
在輸入框改變color的值不會反映到指令中去。
緣由在於,這裏咱們將scope設置爲{},產生了隔離做用域。
因此在template模板中{{color}}變成了依賴於本身的做用域,而不是依賴於父做用域。
所以咱們須要一些辦法來讓隔離做用域能讀取父做用域的屬性,就是綁定策略。
下面咱們就來探索設置這種綁定的幾種方法
方法一:使用@(@attr)來進行單向文本(字符串)綁定
1 <!doctype html> 2 <html ng-app="myApp"> 3 <head> 4 <script src="http://cdn.staticfile.org/ang... 5 </head> 6 <body> 7 8 <div ng-controller='MainController'> 9 10 <input type="text" ng-model="color" placeholder="Enter a color"/> 11 <hello-world color-attr='{{color}}'></hello-world> //注意這裏設置了color-attr屬性,綁定了{{color}} 12 </div> 13 14 <script> 15 16 var app = angular.module('myApp',[]); 17 app.controller('MainController',function(){}); 18 app.directive('helloWorld',function(){ 19 return { 20 scope: {color:'@colorAttr'}, //指明瞭隔離做用域中的屬性color應該綁定到屬性colorAttr 21 restrict: 'AE', 22 replace: true, 23 template: '<p style="background-color:{{color}}">Hello World</p>' 24 } 25 }); 26 </script> 27 28 </body> 29 </html>
這種辦法只能單向,經過在運行的指令的那個html標籤上設置color-attr屬性,而且採用{{}}綁定某個模型值。
注意,你也能夠再這裏直接綁定字符串的顏色值,如:color-attr=「red」;但顏色就是固定值了。
當表達式的值發生變化時,屬性color-attr也會發生變化,因此也改變了隔離做用域中的屬性color。
tips:若是綁定的隔離做用域屬性名與元素的屬性名相同,則能夠採起缺省寫法。
1 html:<hello-world color="{{color}}"/> 2 3 js定義指令的片斷:app.directive('helloWorld',function(){ 4 5 return { 6 scope: { 7 color: '@' 8 }, 9 ... 10 //配置的餘下部分 11 } 12 });
方法二:使用=(=attr)進行雙向綁定
1 <div ng-controller='MainController'> 2 3 <input type="text" ng-model="color" placeholder="Enter a color"/> 4 {{color}} 5 <hello-world color='color'></hello-world> //注意這裏的寫法 6 </div> 7 8 <script> 9 10 var app = angular.module('myApp',[]); 11 app.controller('MainController',function(){}); 12 app.directive('helloWorld',function(){ 13 return { 14 scope:{color:'='}, 15 restrict: 'AE', 16 replace: true, 17 template: '<div style="background-color:{{color}}">Hello World<div><input type="text" ng-model="color"></div></div>' 18 } 19 }); 20 </script>
這樣一個雙向綁定被創建了,改變任何一個input都會改變另外一個值。
方法三:使用&來調用父做用域中的函數
1 <div ng-controller='MainController'> 2 3 <input type="text" ng-model="name" placeholder="Enter a color"/> 4 {{name}} 5 <hello-world saysomething999="say();" name="liucunjie"></hello-world> //注意這裏 6 </div> 7 8 <script> 9 10 var app = angular.module('myApp',[]); 11 app.controller('MainController',function($scope){ 12 $scope.say = function(){ 13 alert('hello'); 14 } 15 $scope.name = 'leifeng'; 16 }); 17 app.directive('helloWorld',function(){ 18 return { 19 scope:{ 20 saysomething:'&saysomething999', 21 name:'@' 22 }, 23 restrict: 'AE', 24 replace: true, //這裏設置爲true表示什麼(單項綁定:....) 25 template: '<button type="button" ng-bind="name" ng-init="saysomething();"></button>' 26 } 27 }); 28 </script>
運行以後,彈出alert框。
7.transclude
(布爾值或者字符‘element’),默認值爲false;
這個配置選項可讓咱們提取包含在指令那個元素裏面的內容,再將它放置在指令模板的特定位置。
當你開啓transclude後,你就可使用ng-transclude來指明瞭應該在什麼地方放置transcluded內容.
1 <div ng-controller='MainController'> 2 3 <div class='a'> 4 <p>china</p> 5 <hello-world> 6 {{name}} 7 </hello-world> 8 </div> 9 </div> 10 11 <script> 12 13 var app = angular.module('myApp',[]); 14 app.controller('MainController',function($scope){ 15 $scope.name = 'leifeng'; 16 }); 17 app.directive('helloWorld',function(){ 18 return { 19 scope:{}, 20 restrict: 'AE', 21 transclude: true, 22 template: '<div class="b"><div ng-transclude>你看不見我</div></div>' 23 } 24 }); 25 </script>
另外當開啓transclude,會建立一個新的transclude空間,而且繼承了父做用域(即便Scope設置爲隔離做用域),
上面代碼中的{{name}}是依賴於父做用域的,仍然能被渲染出來,就說明了這點。
咱們再看看生成的html爲下圖所示,能夠發現文本「你看不見我」消失了,這是由於被transclude內容替換掉了。
8.controller
能夠是一個字符串或者函數。
如果爲字符串,則將字符串當作是控制器的名字,來查找註冊在應用中的控制器的構造函數
1 angular.module('myApp', []) 2 .directive('myDirective', function() { 3 restrict: 'A', // 始終須要 4 controller: 'SomeController' 5 }) 6 // 應用中其餘的地方,能夠是同一個文件或被index.html包含的另外一個文件 7 angular.module('myApp') 8 .controller('SomeController', function($scope, $element, $attrs, $transclude) { 9 // 控制器邏輯放在這裏 10 }); 11
也能夠直接在指令內部的定義爲匿名函數,一樣咱們能夠再這裏注入任何服務($log,$timeout等等) 12 13 angular.module('myApp',[]) 14 .directive('myDirective', function() { 15 restrict: 'A', 16 controller: 17 function($scope, $element, $attrs, $transclude) { 18 // 控制器邏輯放在這裏 19 } 20 });
另外還有一些特殊的服務(參數)能夠注入
(1)$scope,與指令元素相關聯的做用域
(2)$element,當前指令對應的 元素
(3)$attrs,由當前元素的屬性組成的對象
(4)$transclude,嵌入連接函數,實際被執行用來克隆元素和操做DOM的函數
注意: 除非是用來定義一些可複用的行爲,通常不推薦在這使用。
<my-site site="http://www.cnblogs.com/yoissee"><div>coder——231的博客</div></my-site>
1 angular.module('myApp',[]).directive('mySite', function () { 2 return { 3 restrict: 'EA', 4 transclude: true, //注意此處必須設置爲true 5 controller: 6 function ($scope, $element,$attrs,$transclude,$log) { //在這裏你能夠注入你想注入的服務 7 $transclude(function (clone) { 8 var a = angular.element('<a>'); 9 a.attr('href', $attrs.site); 10 a.text(clone.text()); //這裏的clone.text()就是 coder_231的博客 11 $element.append(a); 12 }); 13 $log.info("hello everyone"); 14 } 15 };
注意:使用$transclude會生成一個新的做用域。
默認狀況下,若是咱們簡單實用$transclude(),那麼默認的其做用域就是$transclude生成的做用域
可是若是咱們實用$transclude($scope,function(clone){}),那麼做用域就是directive的做用域了
那麼問題又來了。若是咱們想實用父做用域呢
可使用$scope.$parent
1 <div ng-controller='parentctrl'> 2 3 <div ng-controller='sonctrl'> 4 <my-site site="http://www.cnblogs.com/cunjieliu"><div>coder的博客</div></my-site> 5 </div> 6 </div> <script> 7 8 var app = angular.module('myApp',[]); 9 app.controller('sonctrl',function($scope){ 10 $scope.title = 'hello son'; 11 }); 12 app.controller('parentctrl',function($scope){ 13 $scope.title = 'hello parent'; 14 }); 15 app.directive('mySite', function () { 16 return { 17 restrict: 'EA', 18 transclude: true, 19 controller: 20 function ($scope, $element,$attrs,$transclude,$log) { 21 var a = $transclude(); 22 $element.append(a); 23 $log.info($scope.title); //‘hello son' 24 $log.info($scope.$parent.title); //hello parent 25 } 26 }; 27 }); 28 </script>
9.controllerAs
這個選項的做用是能夠設置你的控制器的別名
通常之前咱們常常用這樣方式來寫代碼:
1 angular.module("app",[]) .controller("demoController",["$scope",function($scope){ 2 3 $scope.title = "angualr"; 4 }]) 5 6 <div ng-app="app" ng-controller="demoController"> 7 8 {{title}} 9 </div>
後來angularjs1.2給咱們帶來新語法糖,因此咱們能夠這樣寫
1 angular.module("app",[]) .controller("demoController",[function(){ 2 3 this.title = "angualr"; 4 }]) 5 6 <div ng-app="app" ng-controller="demoController as demo"> 7 8 {{demo.title}} 9 </div>
10.require(字符串或者數組)
字符串表明另外一個指令的名字,它將會做爲link函數的第四個參數
具體用法咱們能夠舉個例子說明
假設如今咱們要編寫兩個指令,兩個指令中的link連接函數中(link函數後面會講)存在有不少重合的方法,
這時候咱們就能夠將這些重複的方法寫在第三個指令的controller中(上面也講到controller常常用來提供指令間的複用行爲)
而後在這兩個指令中,require這個擁有controller字段的的指令(第三個指令),
最後經過link連接函數的第四個參數就能夠引用這些重合的方法了。
1 <outer-directive> 2 3 <inner-directive></inner-directive> 4 <inner-directive2></inner-directive2> 5 </outer-directive> 6 <script> 7 var app = angular.module('myApp', []); 8 app.directive('outerDirective', function() { 9 return { 10 scope: {}, 11 restrict: 'AE', 12 controller: function($scope) { 13 this.say = function(someDirective) { 14 console.log('Got:' + someDirective.message); 15 }; 16 } 17 }; 18 }); 19 app.directive('innerDirective', function() { 20 return { 21 scope: {}, 22 restrict: 'AE', 23 require: '^outerDirective', 24 link: function(scope, elem, attrs, controllerInstance) { 25 scope.message = "Hi,leifeng"; 26 controllerInstance.say(scope); //打印hi.leifeng 27 } 28 }; 29 }); 30 app.directive('innerDirective2', function() { 31 return { 32 scope: {}, 33 restrict: 'AE', 34 require: '^outerDirective', 35 link: function(scope, elem, attrs, controllerInstance) { //第4個參數指向的就是那個controller 36 scope.message = "Hi,shushu"; 37 controllerInstance.say(scope); 38 } 39 }; 40 }); 41 42 </script>
上面例子中的指令innerDirective和指令innerDirective2複用了定義在指令outerDirective的controller中的方法
也進一步說明了,指令中的controller是用來讓不一樣指令間通訊用的。
另外咱們能夠在require的參數值加上下面的某個前綴,這會改變查找控制器的行爲:
(1)沒有前綴,指令會在自身提供的控制器中進行查找,若是找不到任何控制器,則會拋出一個error
(2)?若是在當前的指令沒有找到所需的控制器,則會將null傳給link鏈接函數的第四個參數
(3)^若是在當前的指令沒有找到所需的控制器,則會查找父元素的控制器
(4)?^組合
11.Anguar的指令編譯過程
首先加載angularjs庫,查找到ng-app指令,從而找到應用的邊界,根據ng-app劃定的做用域來調用$compile服務進行編譯,angularjs會遍歷整個HTML文檔,並根據js中指令
的定義來處理在頁面上聲明的各個指令按照指令的優先級(priority)排列,根據指令中的配置參數(template,place,transclude等)轉換DOM,而後就開始按順序執行各指令的
compile函數(若是指令上有定義compile函數)對模板自身進行轉換。
注意:此處的compile函數是咱們指令中配置的,跟上面說的$compile服務不同。
每一個compile函數執行完後都會返回一個link函數,全部的link函數會合成一個大的link函數而後這個大的link函數就會被執行,主要作數據綁定,經過在DOM上註冊監聽器來動態
修改scope中的數據,或者是使用$watchs監聽 scope中的變量來修改DOM,從而創建雙向綁定等等。若咱們的指令中沒有配置compile函數,那咱們配置的link函數就會運行,
她作的事情大體跟上面complie返回以後全部的link函數合成的的大的link函數差很少。
因此:在指令中compile與link選項是互斥的,若是同時設置了這兩個選項,那麼就會把compile所返回的函數當作是連接函數,而link選項自己就會被忽略掉。