再來繼續介紹 directive 屬性吧,補上 初談 directive 順序圖。
(為了教學流暢,介紹順序有點變更。數字不會變!)
8. transclude (default false)
使用 transclude 屬性可以讓 directive 的 template 包覆宣告該 directive element 裡的內容。
(假如沒有宣告 transclude,directive element 裡的內容被 directive template 完全覆蓋掉)
有兩種寫法:
transclude: true,directive template 會包覆 element 內容。
範例:http://plnkr.co/edit/zbI4jLuqalJJNQTPwHjA
app.directive('myDirective', function() {
return {
restrict: 'EA',
scope: {},
transclude: true,
// 一定要定義 ng-transclude attr,來指定安插內容的點
template: ''
};
});
結果:element 會包圍 directive template,並且把內容塞在 ng-transclude
transclude: ‘element’,整個 element 會被包進去
http://plnkr.co/edit/2woXMWrScVQ1oPb7OT8g
app.directive('myDirective', function() {
return {
restrict: 'EA',
scope: {},
transclude: 'element',
// 搭配 replace: true 讓此 directive 有新的 rootnode
replace: true,
// 一定要定義 ng-transclude attr,來指定安插內容的點
template: '',
link: function (scope, element, attrs, ctrl, transclude) {
// 也可以從 link 或 controller 取得 transclude 操作
transclude(scope, function (clone) {
element.after('<div> append from link function </div>');
})
}
};
});
結果:element 會整個被包在 ng-transclude 裡面
7.1 controller
directive controller 裡頭可放置跟其他 directive 分享的 method 或參數,方便讓 directive 彼此溝通。
(互相溝通的宣告請看 9.require 屬性)
在 directive 的 controller 裡頭可以 DI,$scope, $element, $attrs, $transclude (用法跟 link function 一樣)
7.2 controllerAs
需要跟 controller 同時出現,定義 controller 的別名。
app.directive('myDirective', function() {
return {
scope: {},
controller: function () {
this.value = “hello”;
},
controllerAs: 'MyCtrl'
}
<div>
<!--取得 controller 裡頭的值 -->
<input type="text" ng-model="MyCtrl.value">
</div>
9. require
宣告該 directive 是否依賴其他 directive 的 controller 屬性
該 directive 可以在 link function 的地四個參數中取得依賴的 directive 的 controller function
宣告方式有四種:
我目前實作 require 屬性的 directive 都是一定要找到依賴的 directive,所以幾乎沒有用加 ? 的版本。
範例用法:製作 tab 系列的 directives,需要兩種 directives。
tab 可以有多個,然後每個 tab 需要依賴 tabset 裡頭 controller 才分享資料跟溝通。
這時候在 tab 就需要 require tabset directive。
<tabset>
<tab></tab>
<tab></tab>
</tabset>
app.directive('tab’, function() {
return {
require: '^tabset',
<...>
});
完整範例:angular-foundation Tab directive
10. link
function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
link function 大家應該很熟悉,專門來放操作 DOM 事情跟其他 directive 需要知道的邏輯。
之前看到有人會把對 DOM 操作、$watch 等邏輯放在 link,然後使用者互動邏輯放在 controller。
見仁見智,我通常都是 link function 裡頭有超過五個以上的 methods 才會想要拆開來~
11. compile
function compile(tElement, tAttrs, transclude) { ... }
compile 屬性跟 link 屬性只能兩選一。
因為 compile 規定要回傳 link function,所以假如 directive 有定義 compile 屬性,compile return 的值會覆寫你另外定義的 link function 。
compile 最常使用情境是當 template DOM 還沒 render 前,我們需要 scope 或 attributes 來生成 templates 需要的值。
範例:使用 compile 生成亂數的 id,讓畫面顯示
http://plnkr.co/edit/S93cquFdydntS0e1aDxT
app.directive('myDirective', function() {
return {
scope: {},
compile: function () {
// return link function
return {
pre: function (scope, element, attrs) {
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for ( var i = 0; i < 5; i++ ) {
scope.rId += possible.charAt(Math.floor(Math.random() * possible.length));
}
},
post: function (scope) {
scope.title = 'hello';
}
}
},
template:
'<div>' +
'<label for="{{rId}}" type="text">名稱</label>' +
'<input id="{{rId}}" type="text" ng-model="title"/>' +
'</div>'
};
});
個人很少使用到 compile 屬性,幾乎都是用 link + controller 完成 directive。
屬性呼叫順序:(假設 priority 相同)
parent compile -> children compile -> parent controller -> parent pre-link -> children controller -> children pre-link -> children post-link -> parent post-link
post-link 是最後一步驟,所以我們可以確保在裡面對 element 綁定事件是 ok 的。
(post-link 就是我們平常是用的 link function~)