app.directive('helloWorld', function() {
return {
restrict: 'AE',
replace: true,
template: '<p style="Hello World',
link: function(scope, elem, attrs) {
elem.bind('click', function() {
elem.css('background-color', 'white');
scope.$apply(function() {
scope.color = "white";
});
});
elem.bind('mouseover', function() {
elem.css('cursor', 'pointer');
});
}
};
});
咱們注意到指令定義中的 link 函數。 它有三個參數:
- scope – 指令的scope。在咱們的例子中,指令的scope就是父controller的scope。
- elem – 指令的jQLite(jQuery的子集)包裝DOM元素。若是你在引入AngularJS以前引入了jQuery,那麼這個元素就是jQuery元素,而不是jQLite元素。因爲這個元素已經被jQuery/jQLite包裝了,因此咱們就在進行DOM操做的時候就不須要再使用 $()來進行包裝。
- attr – 一個包含了指令所在元素的屬性的標準化的參數對象。舉個例子,你給一個HTML元素添加了一些屬性:,那麼能夠在 link 函數中經過 attrs.someAttribute 來使用它。
link函數主要用來爲DOM元素添加事件監聽、監視模型屬性變化、以及更新DOM。在上面的指令代碼片斷中,咱們添加了兩個事件, click,和 mouseover。click 處理函數用來重置 <p> 的背景色,而 mouseover 處理函數改變鼠標爲 pointer。在模板中有一個表達式 {{color}},當父scope中的 color 發生變化時,它用來改變 Hello World 文字的背景色。 這個 plunker 演示了這些概念。
compile函數
compile 函數在 link 函數被執行以前用來作一些DOM改造。它接收下面的參數:函數
- tElement – 指令所在的元素
- attrs – 元素上賦予的參數的標準化列表
要注意的是 compile 函數不能訪問 scope,而且必須返回一個 link 函數。可是若是沒有設置 compile 函數,你能夠正常地配置 link 函數,(有了compile,就不能用link,link函數由compile返回)。compile函數能夠寫成以下的形式:性能
app.directive('test', function() {spa
return {
compile: function(tElem,attrs) {
//do optional DOM transformation here
return function(scope,elem,attrs) {
//linking function here
};
}
};
});
大多數的狀況下,你只須要使用 link 函數。這是由於大部分的指令只須要考慮註冊事件監聽、監視模型、以及更新DOM等,這些均可以在 link 函數中完成。 可是對於像 ng-repeat 之類的指令,須要克隆和重複 DOM 元素屢次,在 link 函數執行以前由 compile 函數來完成。這就帶來了一個問題,爲何咱們須要兩個分開的函數來完成生成過程,爲何不能只使用一個?要回答好這個問題,咱們須要理解指令在Angular中是如何被編譯的!
指令是如何被編譯的
當應用引導啓動的時候,Angular開始使用 $compile 服務遍歷DOM元素。這個服務基於註冊過的指令在標記文本中搜索指令。一旦全部的指令都被識別後,Angular執行他們的 compile 方法。如前面所講的,compile 方法返回一個 link 函數,被添加到稍後執行的 link 函數列表中。這被稱爲編譯階段。若是一個指令須要被克隆不少次(好比 ng-repeat),compile函數只在編譯階段被執行一次,複製這些模板,可是link 函數會針對每一個被複制的實例被執行。因此分開處理,讓咱們在性能上有必定的提升。這也說明了爲何在 compile 函數中不能訪問到scope對象。 在編譯階段以後,就開始了連接(linking)階段。在這個階段,全部收集的 link 函數將被一一執行。指令創造出來的模板會在正確的scope下被解析和處理,而後返回具備事件響應的真實的DOM節點。rest
改變指令的Scope
默認狀況下,指令獲取它父節點的controller的scope。但這並不適用於全部狀況。若是將父controller的scope暴露給指令,那麼他們能夠隨意地修改 scope 的屬性。在某些狀況下,你的指令但願可以添加一些僅限內部使用的屬性和方法。若是咱們在父的scope中添加,會污染父scope。 其實咱們還有兩種選擇:orm
- 一個子scope – 這個scope原型繼承子父scope。
- 一個隔離的scope – 一個孤立存在不繼承自父scope的scope。
這樣的scope能夠經過指令定義對象中 scope 屬性來配置。下面的代碼片斷是一個例子:對象
app.directive('helloWorld', function() {繼承
return {
scope: true, // use a child scope that inherits from parent
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>'
};
});
上面的代碼,讓Angular給指令建立一個繼承自父socpe的新的子scope。 另一個選擇,隔離的scope:
app.directive('helloWorld', function() {
return {
scope: {}, // use a new isolated scope
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>'
};
});
這個指令使用了一個隔離的scope。隔離的scope在咱們想要建立可重用的指令的時候是很是有好處的。經過使用隔離的scope,咱們可以保證咱們的指令是自包含的,能夠被很容易的插入到HTML應用中。 它內部不能訪問父的scope,所保證了父scope不被污染。 在咱們的 helloWorld 指令例子中,若是咱們將 scope 設置成 {},那麼上面的代碼將不會工做。 它會建立一個新的隔離的scope,那麼相應的表達式 {{color}} 會指向到這個新的scope中,它的值將是 undefined. 使用隔離的scope並不意味着咱們徹底不能訪問父scope的屬性。