前言
最近學習了下angularjs指令的相關知識,也參考了前人的一些文章,在此總結下。
歡迎批評指出錯誤的地方。
Angularjs指令定義的API
AngularJs的指令定義大致如下
angular.module("app",[]).directive("directiveName",function(){ return{ //通過設置項來定義 }; })
其中return返回的對象包含很多參數,下面一一說明
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-->
例如restrict:‘EA’ 則表示指令在DOM里面可用元素形式和屬性形式被聲明;
一般來說,當你創建一個有自己模板的組件的時候,需要使用元素名,如果僅僅是為為已有元素添加功能的話,就使用屬性名
注意:如果想支持IE8,則最好使用屬性和類形式來定義。 另外Angular從1.3.x開始, 已經放棄支持IE8了.
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.replace
(布爾值),默認值為false,設置為true時候,我們再來看看下面的例子(對比下在template時候舉的例子)
angular.module("app",[]).directive("hello",function(){ return{ restrict:'EA', replace:true, template:"<div><h3>hello world</h3></div>" }; })
HTML代碼為:
<hello></hello>
渲染之后的代碼:<div><h3>hello world</h3></div>
對比下沒有開啟replace時候的渲染出來的HTML。發現<hello></hello>不見了。
另外當模板為純文本(即template:"wo shi wen ben")的時候,渲染之后的html代碼默認的為文本用span包含。
7.scope
可選參數,(布爾值或者對象)默認值為false,可能取值:
(1)默認值false。
表示繼承父作用域;
(2)true
表示繼承父作用域,並創建自己的作用域(子作用域);
(3){}
表示創建一個全新的隔離作用域;
7.1首先我們先來了解下scope的繼承機制。我們用ng-controller這個指令舉例,
我們都知道ng-controller(內置指令)可以從父作用域中繼承並且創建一個新的子作用域。如下:
<!doctype html> <html ng-app="myApp"> <head> <script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script> </head> <body> <div ng-init="aaa='父親'"> parentNode:{{aaa}} <div ng-controller="myController"> chrildNode: {{aaa}} </div> </div> <script> angular.module('myApp', []) .controller('myController',function($scope){ $scope.aaa = '兒子' }) </script> </body> </html>
這時頁面顯示是
parentNode:父親
chrildNode: 兒子
若去掉
$scope.aaa = '兒子'
則顯示
parentNode:父親
chrildNode: 父親
注意:
1)若一個元素上有多個指令,使用了隔離作用域,則只有其中一個可以生效;
2)只有指令模板中的根元素才能獲得一個新的作用域,這時候,scope就被設置為true了;
<!doctype html> <html ng-app="myApp"> <head> <script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script> </head> <body> <div ng-init="aaa='父親'"> parentNode:{{aaa}} <div class='one' ng-controller="myController"> chrildNode: {{aaa}} <div class='two' ng-controller='myController2'> {{aaa}} </div> </div> </div> <script> angular.module('myApp', []) .controller('myController',function($scope){ $scope.aaa = '兒子'; }) .controller('myController2',function($scope){ $scope.aaa = '孫女'; }) </script> </body> </html>
頁面顯示為:
parentNode:父親
chrildNode: cunjieliu
孫女
上面中class為one那個div獲得了指令ng-controller=’myController‘所創建的新的作用域;
而class為two那個div獲得了指令ng-controller=’myController2‘所創建的新的作用域;
這就是“只有指令模板中的根元素才能獲得一個新的作用域”;
接下來我們通過一個簡單明了的例子來說明scope取值不同的差別
<!doctype html> <html ng-app="myApp"> <head> <script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script> </head> <body> <div ng-controller='MainController'> 父親: {{name}} <input ng-model="name" /> <div my-directive></div> </div> <script> angular.module('myApp', []) .controller('MainController', function ($scope) { $scope.name = 'leifeng'; }) .directive('myDirective', function () { return { restrict: 'EA', scope:false,//改變此處的取值,看看有什么不同 template: '<div>兒子:{{ name }}<input ng-model="name"/></div>' }; }); </script> </body> </html>
依次設置scope的值false,true,{},結果發現(大家別偷懶,動手試試哈)
當為false時候,兒子繼承父親的值,改變父親的值,兒子的值也隨之變化,反之亦如此。(繼承不隔離)
當為true時候,兒子繼承父親的值,改變父親的值,兒子的值隨之變化,但是改變兒子的值,父親的值不變。(繼承隔離)
當為{}時候,沒有繼承父親的值,所以兒子的值為空,改變任何一方的值均不能影響另一方的值。(不繼承隔離)
tip:當你想要創建一個可重用的組件時隔離作用域是一個很好的選擇,通過隔離作用域我們確保指令是‘獨立’的,並可以輕松地插入到任何HTML app中,並且這種做法防止了父作用域被污染;
7.2隔離作用域可以通過綁定策略來訪問父作用域的屬性。
下面看一個例子
<!doctype html> <html ng-app="myApp"> <head> <script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script> </head> <body> <div ng-controller='MainController'> <input type="text" ng-model="color" placeholder="Enter a color"/> <hello-world></hello-world> </div> <script> var app = angular.module('myApp',[]); app.controller('MainController',function(){}); app.directive('helloWorld',function(){ return { scope: false, restrict: 'AE', replace: true, template: '<p style="Hello World</p>' } }); </script> </body> </html>
運行代碼,並在input中輸入顏色值,結果為
但是,但我們將scope設置為{}時候,再次運行上面的代碼可以發現頁面並不能成功完整顯示!
原因在於,這里我們將scope設置為{},產生了隔離作用域。
所以在template模板中{{color}}變成了依賴於自己的作用域,而不是依賴於父作用域。
因此我們需要一些辦法來讓隔離作用域能讀取父作用域的屬性,就是綁定策略。
下面我們就來探索設置這種綁定的幾種方法
方法一:使用@(@attr)來進行單向文本(字符串)綁定
<!doctype html> <html ng-app="myApp"> <head> <script src="http://cdn.staticfile.org/angular.js/1.2.10/angular.min.js"></script> </head> <body> <div ng-controller='MainController'> <input type="text" ng-model="color" placeholder="Enter a color"/> <hello-world color-attr='{{color}}'></hello-world> //注意這里設置了color-attr屬性,綁定了{{color}} </div> <script> var app = angular.module('myApp',[]); app.controller('MainController',function