在Angular當(dāng)中,指令是可以用來實(shí)現(xiàn)你想做的任何事情的利器,它可以給Dom綁定你所指定的行為(例如事件監(jiān)聽、添加點(diǎn)擊事件),甚至是它都可以直接轉(zhuǎn)換Dom元素(例如直接將<div>標(biāo)簽轉(zhuǎn)換成一個列表)。
而在原生的Angular中自帶了很多的指令,例如ngModel、ngClass、ngRepeat等等。但有時(shí)官方的指令滿足不了我們的需求,這時(shí)就需要創(chuàng)建特定的指令,例如當(dāng)前項(xiàng)目中的手機(jī)號限制指令、富文本字?jǐn)?shù)限制指令、模糊搜索指令等等。而且,指令可以減少重復(fù)邏輯代碼的編寫,方便使用,提升開發(fā)效率。所以,學(xué)習(xí)下如何自定義指令是很有必要的。
那么我們?nèi)绾巫远x指令?
指令的一般模板
var myModule = angular.module([]);myModule.directive('myDirective', function() { return { restrict: 'EA', priority: 1000, template: '<div></div>', templateUrl: 'test.html', replace: true, transclude: true, scope: true, controller: function ($scope, $element, $attrs, $transclude) { }, require: 'ngRepeat', link: function (scope, iElement, iAttrs) { } };});
它里面有很多屬性,每個屬性的作用如下圖:

實(shí)現(xiàn)一個指令
第一個指令,helloWorld
實(shí)現(xiàn)指令的代碼如下:
demoApp.directive('helloWorld', function () {
return {
restrict: 'E',
template: '<h4>Hello world</h4>'
}
});
當(dāng)實(shí)現(xiàn)之后,我們就可以在我們的html中使用helloWorld指令了,如下:
<div>
<hello-world></hello-world>
</div>
在上面的helloWorld指令中,我們用到下面一些內(nèi)容:
指令命名
在指令的定義中使用的是駝峰式命名,在模板使用時(shí),使用
的是通過短橫線連接的。主要是能夠支持HTML校驗(yàn)規(guī)則,我
們這里使用的是H5的HTML規(guī)則。
例如,上面的helloWorld指令,我們在定義指令的時(shí)候,使用的是hellWorld這種駝峰式,而我們在模板中使用指令的時(shí)候是<hello-world></hello-world>,使用的是用短橫線連接。
restrict屬性
它描述指令的聲明風(fēng)格,即它是否可以作元素名稱、元素屬性、樣式類或者注釋。

注意:當(dāng)想讓一個指令可以作為元素,也可以作為屬性時(shí),可以使用“AE”
例如,上面的helloWolrd指令,只是作為一個元素,因?yàn)樗膔estrict為E。
模板
模板有兩種形式:
- template:以字符串形式編寫的一個內(nèi)聯(lián)模板
- templateUrl:加載模板的URL
上面的helloWorld指令,使用的是template屬性,直接填充<h4>Hello world</h4>字符串作為指令的內(nèi)容。
最終,我們的helloWorld使用的,達(dá)到的效果如下:

除了,上面helloWorld使用的restrict和template外,還有一些重要的屬性,例如replace、transclude、scope、link,我們都來學(xué)習(xí)下。
replace
為true,則替換指令所在的元素;為false,則把當(dāng)前指令追加到指令所在的元素內(nèi)部,默認(rèn)false。
若是,將helloWorld指令replace設(shè)置為true,代碼如下:
demoApp.directive('helloWorld', function () { return { restrict: 'E', template: '<h4>Hello world</h4>', replace: true }});
指令轉(zhuǎn)換結(jié)果:

transclude
為true,則把指令所在元素中原來的子節(jié)點(diǎn)移動到ng-transclude所在元素內(nèi);為false,則直接忽略內(nèi)部的子節(jié)點(diǎn),默認(rèn)false。
若是將helloWorld指令transclude設(shè)置為true,它的代碼如下:
demoApp.directive('helloWorld', function () { return { restrict: 'E', template: '<h4>Hello world<span ng-transclude style="color: red"></span></h4>', replace: true, transclude: true }});
指令轉(zhuǎn)換結(jié)果:

scope屬性
指令的作用域,默認(rèn)false。當(dāng)為false,為所在Dom元素上的作用域;當(dāng)設(shè)置true時(shí),創(chuàng)建一個新的作用域,所在元素的作用域是它的父作用域,它繼承父作用域上所有的屬性;當(dāng)設(shè)置scope為一個對象時(shí),創(chuàng)建一個獨(dú)立的作用域,所在元素的作用域仍然是它的parent,但它不繼承父對象上任何屬性。
當(dāng)scope設(shè)置false時(shí)
html代碼:
<body ng-controller="demoController">
<div>指令外: {{name}}</div>
<hello-world>測試</hello-world>
</body>
js代碼:
var demoApp = angular.module('demoApp', []);
demoApp.controller('demoController', function ($scope) {
$scope.name = "Jack";
});
demoApp.directive('helloWorld', function () {
return {
restrict: 'E',
template: '<h4>指令內(nèi)部: {{name}}</h4>',
replace: true,
transclude: true,
scope: false,
link: function (scope) {
scope.name = "test";
}
}
});
效果如下:

解析:因?yàn)橹噶钔夂椭噶顑?nèi)部的是同一個作用域,所以指令內(nèi)部的name改變,也造成指令外面變化。
當(dāng)scope設(shè)置true時(shí)
html代碼:
<body ng-controller="demoController">
<div>指令外: {{name}}</div>
<hello-world>測試</hello-world>
</body>
js中,指令的實(shí)現(xiàn)代碼如下:
var demoApp = angular.module('demoApp', []);demoApp.controller('demoController', function ($scope) { $scope.name = "Jack";});demoApp.directive('helloWorld', function () { return { restrict: 'E', template: '<h4>指令內(nèi)部, 改變之前:{{preName}}, 改變之后: {{name}}</h4>', replace: true, transclude: true, scope: true, link: function (scope) { scope.preName = scope.name; scope.name = "test"; } }});
結(jié)果:

解析:指令內(nèi)部改變之前的名字是Jack,與外面一樣;改變之后,指令外部沒有變化,仍然是test??梢苑治龀?,當(dāng)設(shè)置為true時(shí),指令內(nèi)部繼承了外部的屬性,但它是一個新的作用域。還可以通過console.log打印進(jìn)一步驗(yàn)證,外面的作用域是內(nèi)部作用域的parent。
scope設(shè)置一個對象

使用時(shí),在html中的代碼如下:
<body ng-controller="demoController"> <div>指令外: {{name}}</div> <hello-world new-name="name">測試</hello-world></body>
js中,指令的實(shí)現(xiàn)代碼如下:
var demoApp = angular.module('demoApp', []);demoApp.controller('demoController', function ($scope) { $scope.name = "Jack";});demoApp.directive('helloWorld', function () { return { restrict: 'E', template: '<h4>指令內(nèi)部, name:{{name}}, scopeName: {{scopeName}}</h4>', replace: true, transclude: true, scope: {scopeName: '=newName'}, link: function (scope) { scope.name = scope.name; scope.scopeName = "test"; } }});
結(jié)果:

解析:name內(nèi)容不存在,說明沒有繼承父scope的屬性;因?yàn)槭褂谩?newName’綁定了外部屬性’name’,所以內(nèi)部改變,指令外的值也變化了。
link函數(shù)
鏈接函數(shù),在鏈接階段執(zhí)行,可以在內(nèi)部操作Dom、添加事件監(jiān)聽、設(shè)置數(shù)據(jù)綁定等等。
例如,實(shí)現(xiàn)的一個限制手機(jī)號的指令,解決了當(dāng)input為num類型時(shí)不能限制11位數(shù)字的問題。內(nèi)部使用element.on監(jiān)聽Dom事件,使用element.after添加元素。
html使用代碼:
<input type="text" ng-model="phone" placeholder="請輸入手機(jī)號" phone-limit/>
js中,指令的實(shí)現(xiàn)代碼如下:
.directive('phoneLimit', function () { return { restrict: "A", scope: { model: '=ngModel' }, link: function (scope, element, attrs, ctrl) { // console.log(element) //添加一個錯誤提示元素 function appendErrorTip() { var msg = "<span class='error-tip'></span>"; element.after(msg); // $(element).after(msg); } //輸入限制 function inputLimit() { var value = String(element.val()); //截取到哪一個字符位置 var findIndex = -1; for(var i = 0; i < value.length; i++) { if(value[i] < '0' || value[i] > '9') { findIndex = i; break; } if(i >= 11) { findIndex = i; break; } } //查找到了, 則截取 if(findIndex != -1) { element.val(value.substr(0, findIndex)); scope.model = element.val(); } } //綁定輸入事件 element.on('input', inputLimit); //添加錯誤提示元素 appendErrorTip(); element.on("blur", function () { var value = element.val(); //驗(yàn)證不通過,標(biāo)紅 if(value == undefined || value.length != 11) { element.next().html('手機(jī)號不正確'); } }) } }});
指令間通信
用到了兩個屬性,如下:
- controller:創(chuàng)建一個控制器,通過這個控制器,可以實(shí)現(xiàn)指令之間的通信
- require:要求必須存在另一個指令,當(dāng)前指令才能正確運(yùn)行
主要是通過控制器進(jìn)行通信的,通過require屬性語法,可以把require中的指令的控制器傳遞給當(dāng)前指令,從而實(shí)現(xiàn)了通信。
其中,require的用法:
如:require: '?^^myTabs'
^^前綴:說明angular會從所有父元素上查找到myTabs指令,并且獲取myTabs指令的控制器;當(dāng)是^前綴時(shí),說明angular會從當(dāng)前元素和所有父元素上查找到myTabs指令,并且獲取myTabs指令的控制器;當(dāng)沒有^和^^時(shí),則說明angular只會從當(dāng)前元素查找到myTabs指令,并且獲取myTabs指令的控制器。
?前綴:可選的意思。具體是,當(dāng)沒有?前綴時(shí),angular按照上面的規(guī)則沒有查找到需要的控制器,那么就會拋出異常;如果有?前綴時(shí),則就算沒查找到,也不會拋出異常。