angularJS系列之指令directive應用例項
程式碼結構
html頁面程式碼
<!DOCTYPE html>
<html ng-app="demoForDirective">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<script src="./lib/angular.js"></script>
<script src="./js/app.js"></script>
<script src="./js/controller.js" ></script>
</head>
<body>
<div ng-controller='demoForDirectiveController'>
<!--增加指令標籤-->
<testdirective testdirective-title='title'>
{{text}}
</testdirective>
</div>
<div>
<!--為原生標籤增加動態屬性-->
分數:<input type="text" attrsdirective/><br/>
</div>
<!--通過控制器控制子元素-->
<div ng-controller="SomeController">
<accordion>
<expander class="expander"
ng-repeat="expander in expanders"
expander-title="expander.title">
{{expander.text }}
</expander>
</accordion>
</div>
</body>
</html>
app.js程式碼
/**
* Created by jywl on 2016/7/7.
*/
var demoForDirective = angular.module('demoForDirective', ['demoForDirective.controller']);
demoForDirective.directive('testdirective', function () {
return {
restrict: 'EA',
replace: true,
transclude: true,
scope: {
title: '=testdirectiveTitle'
},
template: '<div>'
+ '<div class="title" ng-click="toggle()">{{title}}</div>'
+ '<div class="body" ng-show="showMe" ng-transclude></div>'
+ '</div>',
link: function (scope, element, attrs) {
scope.showMe = false;
scope.toggle = function toggle() {
scope.showMe = !scope.showMe;
}
}
}
});
demoForDirective.directive('attrsdirective', function () {
return {
link: function (scope, elements, attrs, controller) {
elements[0].onkeyup = function () { //從元素列表中獲取元素,並繫結相應的事件
//下面的意思是,如果輸入10以內的數,則輸入框邊框顏色不變,否則變為紅色
if (isNaN(this.value) || this.value < 1 || this.value > 10) {
this.style.borderColor = 'red';
}
else {
this.style.borderColor = '';
}
};
}
}
});
demoForDirective.directive('accordion', function () {
return {
restrict: 'EA',
replace: true,
transclude: true,
template: '<div ng-transclude></div>',
controller: function () { //宣告一個內部的controller,用於傳遞值和方法
var expanders = [];
this.gotOpened = function (selectedExpander) {
//angularJS的forEach用法
/*var objs =[{a:1},{a:2}];
angular.forEach(objs, function(data,index,array){
//data等價於array[index]
console.log(data.a+'='+array[index].a);
});
引數如下:
objs:需要遍歷的集合
data:遍歷時當前的資料
index:遍歷時當前索引
array:需要遍歷的集合,每次遍歷時都會把objs原樣的傳一次。
* */
angular.forEach(expanders, function (expander) {
if (selectedExpander != expander) {
expander.showMe = false;
}
});
}
this.addExpander = function (expander) {
expanders.push(expander);
}
}
}
});
demoForDirective.directive('expander', function () {
return {
restrict: 'EA',
replace: true,
transclude: true,
require: '^?accordion',//意思是將accordion的控制器傳到指令中,從而在下方使用它的函式 ^的意思是需要遍歷dom樹,?的意思是找不到不報錯
scope: {
title: '=expanderTitle'
},
template: '<div>'
+ '<div class="title" ng-click="toggle()">{{title}}</div>'
+ '<div class="body" ng-show="showMe" ng-transclude></div>'
+ '</div>',
link: function (scope, element, attrs, accordionController) {
scope.showMe = false;
accordionController.addExpander(scope);
scope.toggle = function toggle() {
scope.showMe = !scope.showMe;
accordionController.gotOpened(scope);
}
}
}
});
controller.js程式碼
/**
* Created by jywl on 2016/7/7.
*/
angular.module('demoForDirective.controller', [])
.controller('demoForDirectiveController', function ($scope) {
$scope.title = '點選展開';
$scope.text = '這裡是內部的內容。';
})
.controller('SomeController', function ($scope) {
$scope.expanders = [
{title: 'click me', text: 'one click'},
{title: 'click me two', text: 'two click'},
{title: 'click me three', text: 'three click'}];
});
程式碼說明
主要詳解app.js中關於指令的程式碼,大部分程式碼都有註釋,如果還有不清楚的可以留言,我看到會及時回覆的。
directive的引數說明
priority(優先順序)- 當有多個directive定義在同一個DOM元素時,有時需要明確它們的執行順序。這屬性用於在directive的compile function呼叫之前進行排序。如果優先順序相同,則執行順序是不確定的(經初步試驗,優先順序高的先執行,同級時按照類似棧的“後繫結先執行”。另外,測試時有點不小心,在定義directive的時候,兩次定義了一個相同名稱的directive,但執行結果發現,兩個compile或者link function都會執行)。據官方說,ng-repeate的優先順序是1000。該屬性預設值為0。
terminal(最後一組)- 如果設定為”true”,則表示當前的priority將會成為最後一組執行的directive。任何directive與當前的優先順序相同的話,他們依然會執行,但順序是不確定的(雖然順序不確定,但基本上與priority的順序一致。當前優先順序執行完畢後,更低優先順序的將不會再執行)。
scope - 如果設定為:
true - 將為這個directive建立一個新的scope作用域,而不是繼承父作用域。
false - 預設值,使用現有的scope作用域。
{/屬性名和繫結風格/} - 獨立的scope。
獨立scope與一般的scope的區別在於它不是通過原型繼承於父scope的。這對於建立可複用的元件是很有幫助的,可以有效防止讀取或者修改父級scope的資料。
繫結策略
@或@attr - 建立一個local scope property到DOM屬性的繫結。因為屬性值總是String型別,所以這個值總是返回一個字串。如果沒有通過@attr指定屬性名稱,那麼本地名稱將與DOM屬性的名稱一致。例如<widget my-attr=”hello {{name}}”>
,widget的scope定義為:{localName:’@myAttr’}
。那麼,widget scope property的localName會映射出”hello {{name}}”轉換後的真實值。name屬性值改變後,widget scope的localName屬性也會相應地改變(僅僅單向,與下面的”=”不同)。name屬性是在父scope讀取的。
=或=expression(綁定當前屬性,它帶來一個來自指令父scope的屬性) - 在本地scope屬性與parent scope屬性之間設定雙向的繫結。如果沒有指定attr名稱,那麼本地名稱將與屬性名稱一致。例如,widget定義的scope為:{localModel:’=myAttr’},那麼widget scope property “localName”將會對映父scope的“parentModel”。如果parentModel發生任何改變,localModel也會發生改變,反之亦然。(雙向繫結)
&或&attr - 傳遞一個來自父scope的函式,稍後呼叫。
controller - controller 建構函式。controller會在pre-linking步驟之前進行初始化,並允許其他directive通過指定名稱的require進行共享(看下面的require屬性)。這將允許directive之間相互溝通,增強相互之間的行為。
controller預設注入了以下本地物件:
$scope - 與當前元素結合的scope
$element - 當前的元素
$attrs - 當前元素的屬性物件
$transclude - (A transclude linking function pre-bound to the correct transclusion scope)
require - 請求另外的controller,傳入當前directive的linking function中。require需要傳入一個directive controller的名稱。如果找不到這個名稱對應的controller,那麼將會丟擲一個error。名稱可以加入以下字首:
? - 不要丟擲異常。這使這個依賴變為一個可選項。
^ - 允許查詢父元素的controller
restrict - EACM的子集的字串,它限制directive為指定的宣告方式。如果省略的話,directive將僅僅允許通過屬性宣告:
E - 元素名稱: <my-directive></my-directive>
A - 屬性名: <div my-directive=”exp”></div>
C - class名: <div class=”my-directive:exp;”></div>
M - 註釋 : <!-- directive: my-directive exp -->
template - 如果replace 為true,則將模版內容替換當前的HTML元素,並將原來元素的屬性、class一併遷移;如果為false,則將模版元素當作當前元素的子元素處理。
templateUrl - 與template基本一致,但模版通過指定的url進行載入。因為模版載入是非同步的,所以compilation、linking都會暫停,等待載入完畢後再執行。
replace - 如果設定為true,那麼模版將會替換當前元素,而不是作為子元素新增到當前元素中。(注:為true時,模版必須有一個根節點)
transclude - 編譯元素的內容,使它能夠被directive所用。(如果當前元素,希望其他directive呼叫,就將其設為true,並在元素的節點上,增加<div ng-transclude></div>
就好,更高階的用法,目前我還在學習中)
true - 轉換這個directive的內容。(這個感覺上,是直接將內容編譯後搬入指定地方)
element - 轉換整個元素,包括其他優先順序較低的directive。(像將整體內容編譯後,當作一個整體(外面再包裹p),插入到指定地方)
compile - 編譯函式,整個執行週期中只執行一遍。(一般情況下,用compile即可。)
link - 連結函式,可以多次執行。具體用法參照上文程式碼