angularJs指令深度分析


AngularJs的指令定義大致如下

angular.module("app",[]).directive("directiveName",function(){

return{ //通過設置項來定義 };

})其中return返回的對象包含很多參數,下面一一說明

你知道用AngularJs怎么定義指令嗎?

1.restrict

(字符串)可選參數,指明指令在DOM里面以什么形式被聲明;

取值有:E(元素),A(屬性),C(類),M(注釋),其中默認值為A;

E(元素):<directiveName></directiveName>
A(屬性):<div directiveName='expression'></div>
C(類): <div class='directiveName'></div>
M(注釋):<--directive:directiveName expression-->

2.priority

(數字),可選參數,指明指令的優先級,若在單個DOM上有多個指令,則優先級高的先執行;

設置指令的優先級算是不常用的

比較特殊的的例子是,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選項本身就會被忽略掉。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM