Directive可能是AngularJS中比較復(fù)雜的一個(gè)東西了。一般我們將其理解成指令。AngularJS自帶了不少預(yù)設(shè)的指令,比如ng-app
,ng-controller
這些。可以發(fā)現(xiàn)個(gè)特點(diǎn),AngularJS自帶的指令都是由ng-
打頭的。
那么,Directive究竟是個(gè)怎么樣的一個(gè)東西呢?我個(gè)人的理解是這樣的:將一段html、js封裝在一起,形成一個(gè)可復(fù)用的獨(dú)立個(gè)體,具體特定的功能。下面我們來(lái)詳細(xì)解讀一下Directive的一般性用法。
看下面的代碼:
var myDirective = angular.module('directives', []);
myDirective.directive('directiveName', function($inject) {
return {
template: '<div></div>',
replace: false,
transclude: true,
restrict: 'E',
scope: {},
controller: function($scope, $element) {
},
complie: function(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
},
post: function postLink(scope, iElement, iAttrs, controller) {
}
};
},
link: function(scope, iElement, iAttrs) {
}
};
});
這里直接return了一個(gè)object,對(duì)象中包括比較多的屬性,這些屬性都是對(duì)自定義directive的定義。詳細(xì)的含義,下面會(huì)繼續(xù)說(shuō)明。
return {
name: '',
priority: 0,
terminal: true,
scope: {},
controller: fn,
require: fn,
restrict: '',
template: '',
templateUrl: '',
replace: '',
transclude: true,
compile: fn,
link: fn
}
如上所示,return的對(duì)象中會(huì)有很多的屬性,這行屬性都是用來(lái)定義directive的。下面我們來(lái)一個(gè)個(gè)的說(shuō)明他們的作用。
name
priority
teminal
true
,則表示當(dāng)前的priority將會(huì)成為最后一組執(zhí)行的directive,即比此directive的priority更低的directive將不會(huì)執(zhí)行。同優(yōu)先級(jí)依然會(huì)執(zhí)行,但是順序不確定。scope
true
{}
controller
$scope
與當(dāng)前元素結(jié)合的scope$element
當(dāng)前的元素$attrs
當(dāng)前元素的屬性對(duì)象$transclude
一個(gè)預(yù)先綁定到當(dāng)前scope的轉(zhuǎn)置linking functionrequire
?
不要拋出異常。這將使得這個(gè)依賴變?yōu)橐粋€(gè)可選項(xiàng)^
允許查找父元素的controllerrestrict
E
元素名稱: A
屬性名:C
class名:M
注釋:template
replace
為true,則將模版內(nèi)容替換當(dāng)前的html元素,并將原來(lái)元素的屬性、class一并轉(zhuǎn)移;如果replace
為false,則將模版元素當(dāng)作當(dāng)前元素的子元素處理。templateUrl
replace
transclude
ngTransclude
使用。transclusion的有點(diǎn)是linking function能夠得到一個(gè)預(yù)先與當(dāng)前scope綁定的transclusion function。一般地,建立一個(gè)widget,創(chuàng)建獨(dú)立scope,transclusion不是子級(jí)的,而是獨(dú)立scope的兄弟級(jí)。這將使得widget擁有私有的狀態(tài),transclusion會(huì)被綁定到父級(jí)scope中。(上面那段話沒(méi)看懂。但實(shí)際實(shí)驗(yàn)中,如果通過(guò)調(diào)用myDirective,而transclude設(shè)置為true或者字符串且template中包含的時(shí)候,將會(huì)將的編譯結(jié)果插入到sometag的內(nèi)容中。如果any的內(nèi)容沒(méi)有被標(biāo)簽包裹,那么結(jié)果sometag中將會(huì)多了一個(gè)span。如果本來(lái)有其他東西包裹的話,將維持原狀。但如果transclude設(shè)置為’element’的話,any的整體內(nèi)容會(huì)出現(xiàn)在sometag中,且被p包裹)compile
link
這里關(guān)于directive的scope為一個(gè)object時(shí),有更多的內(nèi)容非常有必要說(shuō)明一下。看下面的代碼:
scope: {
name: '=',
age: '=',
sex: '@',
say: '&'
}
這個(gè)scope
中關(guān)于各種屬性的配置出現(xiàn)了一些奇怪的前綴符號(hào),有=
,@
,&
,那么這些符號(hào)具體的含義是什么呢?再看下面的代碼:
<div my-directive name="myName" age="myAge" sex="male" say="say()"></div>
function Controller($scope) {
$scope.name = 'Pajjket';
$scope.age = 99;
$scope.sex = '我是男的';
$scope.say = function() {
alert('Hello,我是彈出消息');
};
}
可以看出,幾種修飾前綴符的大概含義:
=
: 指令中的屬性取值為Controller中對(duì)應(yīng)$scope上屬性的取值@
: 指令中的取值為html中的字面量/直接量&
: 指令中的取值為Controller中對(duì)應(yīng)$scope上的屬性,但是這個(gè)屬性必須為一個(gè)函數(shù)回調(diào)下面是更加官方的解釋:
=
或者=expression/attr
attr
名稱,那么本地名稱將與屬性名稱一致。{localModel: '=myAttr'}
,那么widget scope property中的localName
將會(huì)映射父scope的parentModel
。如果parentModel
發(fā)生任何改變,localModel也會(huì)發(fā)生改變,反之亦然。即雙向綁定。@
或者@attr
@attr
指定屬性名稱,那么本地名稱將與DOM屬性的名稱一致。{localName: '@myAttr'}
。那么,widget scope property的localName會(huì)映射出"hello "
轉(zhuǎn)換后的真實(shí)值。當(dāng)name屬性值發(fā)生改變后,widget scope的localName屬性也會(huì)相應(yīng)的改變(僅僅是單向,與上面的=
不同)。那么屬性是在父scope讀取的(不是從組件的scope讀取的)&
或者&attr
<widget my-attr="count = count + value">
,widget的scope定義為:{localFn:’increment()’}
,那么isolate scope property localFn
會(huì)指向一個(gè)包裹著increment()表達(dá)式的function。localFn({amount:22})
的方式調(diào)用localFn以指定amount的值。下面的示例都圍繞著上面所作的參數(shù)說(shuō)明而展開(kāi)的。
// 自定義directive
var myDirective = angular.modeule('directives', []);
myDirective.directive('myTest', function() {
return {
restrict: 'EMAC',
require: '^ngModel',
scope: {
ngModel: '='
},
template: '<div><h4>Weather for {{ngModel}}</h4</div>'
};
});
// 定義controller
var myControllers = angular.module('controllers', []);
myControllers.controller('testController', [
'$scope',
function($scope) {
$scope.name = 'this is directive1';
}
]);
var app = angular.module('testApp', [
'directives',
'controllers'
]);
<body ng-app="testApp">
<div ng-controller="testController">
<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>
<span ng-model="city"></span>
</div>
</body>
templateUrl其實(shí)根template功能是一樣的,只不過(guò)templateUrl加載一個(gè)html文件,上例中,我們也能發(fā)現(xiàn)問(wèn)題,template后面根的是html的標(biāo)簽,如果標(biāo)簽很多呢,那就比較不爽了??梢詫⑸侠械模瑃emplate改一下。
myDirective.directive('myTest', function() {
return {
restrict: 'EMAC',
require: '^ngModel',
scope: {
ngModel: '='
},
templateUrl:'../partials/tem1.html' //tem1.html中的內(nèi)容就是上例中template的內(nèi)容。
}
});
//directives.js中定義myAttr
myDirectives.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
myControllers.controller('attrtest',['$scope',
function($scope) {
$scope.naomi = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
$scope.vojta = {
name: 'Vojta',
address: '3456 Somewhere Else'
};
}
]);
// html中
<body ng-app="testApp">
<div ng-controller="attrtest">
<my-attr info="naomi"></my-attr>
</div>
</body>
其運(yùn)行結(jié)果如下:
Name: Naomi Address: 1600 Amphitheatre //有值,因?yàn)閏ustomerInfo定義過(guò)的
Name: Address: //沒(méi)值 ,因?yàn)閟cope重定義后,vojta是沒(méi)有定義的
我們將上面的directive簡(jiǎn)單的改一下,
myDirectives.directive('myAttr', function() {
return {
restrict: 'E',
template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
'Name: {{vojta.name}} Address: {{vojta.address}}'
};
});
運(yùn)行結(jié)果如下:
Name: Address:
Name: Vojta Address: 3456 Somewhere Else
因?yàn)榇藭r(shí)的directive沒(méi)有定義獨(dú)立的scope,customerInfo是undefined,所以結(jié)果正好與上面相反。
transclude的用法,有點(diǎn)像jquery里面的$().html()功能
myDirective.directive('myEvent', function() {
return {
restrict: 'E',
transclude: true,
scope: {
'close': '$onClick' //根html中的on-click="hideDialog()"有關(guān)聯(lián)關(guān)系
},
templateUrl: '../partials/event_part.html'
};
});
myController.controller('eventTest', [
'$scope',
'$timeout',
function($scope, $timeout) {
$scope.name = 'Tobias';
$scope.hideDialog = function() {
$scope.dialogIsHidden = true;
$timeout(function() {
$scope.dialogIsHidden = false;
}, 2000);
};
}
]);
<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>
說(shuō)明:這段html最終的結(jié)構(gòu)應(yīng)該如下所示:
<body ng-app="phonecatApp">
<div ng-controller="eventtest">
<div ng-hide="dialogIsHidden" on-click="hideDialog()">
<span>Check out the contents, {{name}}!</span>
</div>
</div>
</body>
將原來(lái)的html元素中的元素Check out the contents, !
插入到模版的<div ng-transclude></div>
中,還會(huì)另外附加一個(gè)<span>
標(biāo)簽。
myDirective.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添加
myController.controller('directive2',[
'$scope',
function($scope) {
$scope.number = '1111 ';
}
]);
//html
<body ng-app="testApp">
<div ng-controller="directive2">
<example-directive></example-directive>
</div>
</body>
上面小例子的運(yùn)行結(jié)果如下:
Hello 1111 22222 44444 5555 !
由結(jié)果可以看出來(lái),controller先運(yùn)行,compile后運(yùn)行,link不運(yùn)行。
我們現(xiàn)在將compile
屬性注釋掉后,得到的運(yùn)行結(jié)果如下:
Hello 1111 22222 33333 !
由結(jié)果可以看出來(lái),controller先運(yùn)行,link后運(yùn)行,link和compile不兼容。一般地,compile比link的優(yōu)先級(jí)要高。
更多建議: