在介紹directive之前,我想先講講MVC這個框架的相關知識。這樣可以更好的理解angular。
什么是MVC?mvc是一種設計模式,它把應用划分為三個部分,數據(模型),展現層(視圖),控制交互層(控制器),一個時間的發生時這樣的過程:
1.用戶和應用產生交互
2.控制器的事件處理器被觸發
3.控制器從模型中請求數據,並將其交給視圖
4.視圖將數據呈現給用戶。
模型
模型用來存放應用的所有數據對象,模型不必知曉視圖和控制器的細節,模型只需包含數據及直接和這些數據相關的邏輯。任何事件處理代碼、視圖模板,以及那些和模型無關的邏輯都應當隔離在模型之外。將模型和視圖的代碼混在一起,是違反MVC架構原則的。模型是最應該從你的應用中解耦出來的部分。
當控制器從服務端抓取數據或者創建新的記錄時,它就將數據包裝成模型實例,也就是說,我們的數據是面向對象的,任何定義在這個數據模型上的函數或者邏輯都可以直接被調用。
因此,不要這樣做:
var user = users["foo"]; destroyUser(user);
上面的代碼沒有命名空間的概念,並且不是面向對象的。如果在應用中定義了另一個destoryUser()函數的話,兩個函數就會產生沖突。我們應當確保全局變量和函數的個數盡可能少.而要這樣做。
var user = User.find("foo");
user.destroy();
上面的代碼中,destory()函數是存放在命名空間User的實例中的。這種代碼更加清晰,而且非常容易做繼承,類似destory()的這種函數就不用在每個模型中都定義一遍了。
視圖
視圖層是呈現給用戶的,用戶與之產生交互,在JavaScript 應用中,視圖大都是由HTML、CSS和JavaScript模板組成的。除了模板中簡單的條件語句之外,視圖不應當包含任何其他邏輯。
這並不是說MVC不允許包含視覺呈現相關的邏輯,只要這部分邏輯沒有定義在視圖之內即可。我們將視覺呈現邏輯歸類為“視圖助手”(helper):和視圖有關的獨立的小型工具函數。
反例——formatDate()函數直接插入視圖:
// template.html
<div>
<script>
function formatDate(date) {
/* ... */
};
</script>
${ formatDate(this.date) }
</div>
應該這樣做——所有視覺呈現邏輯都包含在helper變量中,這是一個命名空間,可以防止沖突並保持代碼清晰、可擴展:
// helper.js
var helper = {};
helper.formatDate = function(){ /* ... */ };
// template.html
<div>
${ helper.formatDate(this.date) }
</div>
控制器
控制器是模型和視圖之間的紐帶,控制器從視圖獲得時間和輸入,對它們驚醒處理(很可能包含模型),並相應的跟新視圖。當頁面加載工作時,控制器會給視圖添加事件監聽,比如監聽表單提交或者按鈕點擊。然后,當用戶和應用產生交互時,控制器中的事件觸發器就開始工作了。
下面用jQuery實現一個例子:
var Controller = {};
// 使用匿名函數來封裝一個作用域
(Controller.users = function($){
var nameClick = function(){
/* ... */
};
// 在頁面加載時綁定事件監聽
$(function(){
$("#view .name").click(nameClick);
});
})(jQuery);
上面的代碼創建了users控制器,這個控制器是放在Controller變量下的命名空間。然后用了一個匿名函數封裝了一個作用域,以避免對全局作用域造成污染。當頁面加載時,程序給視圖元素綁定了點擊事件的監聽。
上面就是mvc的簡單介紹。下面繼續directive的屬性講解。
基本上每個directive都會經過$compile編譯,然后通過link函數來拓展相應的DOM元素。下面介紹常用的directive定義的對象:
priority
當有多個directive定義在同一個DOM元素上時,有必要指定directive的應用順序。priority對象通常用數字表示,數字越大的,相應的指定便優先編譯,相反該directive對應的link函數便越靠后執行。priority的值默認為是0.
scope(講到這個,值得提一提$scope對象,還有在controller中使用$scope和this(當在html中使用controller as時可以使用)的區別,感興趣的話自行搜索)
它的值有三種;true,false(默認),object(對象)。
什么是angular中的scope?
scope(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope)是一個指向model的object。也是表達式的的執行上下文(請自行了解執行上下文的概念)。angular中提供了一些常用API:$watch API(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$watch),用於監測model的變化,$apply API(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$apply),用於監測angular 定義的對象內外的所有model的變化。
在angular中,子作用域一般是通過原型繼承機制繼承其父作用域的屬性和方法,但是有一個例外:在directive中,使用scope:{...},這種方式創建的作用域是一個獨立的(isolate)作用域,它也有父作用域,但父作用域不在其原型鏈上,不會對父作用域原型繼承,這種方式定義作用域通常用於構造可復用的directive組件,因為這樣定義的scope,不會直接訪問或者時候修改父作用域的屬性,不會產生意外的副作用。
如果我們在子作用域中訪問一個父作用域中定義的屬性,那么程序會首先在子作用域中尋找該屬性,沒找到在從原型鏈上的父作用域中尋找,在沒找到,再往上一級原型鏈找。在angular中,作用域原型鏈的頂端是$rootScope。
在單獨的directive中,scope的概念還是比較清晰的。
當scope取值為false時,此時directive沒有獨立的scope對象,link函數中引用的scope對象為來自於當前節點的默認controller。
當scope取值為true是時,directive擁有獨立的scope對象,此scope是由父scope繼承而來,可以訪問父scope中的所有屬性,此時通過javasript原型繼承。值得注意的是:當給此scope繼承而來的屬性名稱賦值時,子scope會相應建立一個本地屬性,此時改變的是本scope的變量屬性,父scope中的屬性是不會改變的。
當scope取值為{propertyName:"=@propertyValue},此時directive擁有一個隔離的scope對象,其實就是一個全新的scope對象,和上面取值的區別就是不能通過原型繼承訪問父scope中的屬性,但是可以通過$parent屬性去訪問父scope中對象屬性的。
下面講講當scope取值為{...}時,申明scope對象的引用修飾符的用法:(http://stackoverflow.com/questions/14050195/angularjs-what-is-the-difference-between-and-in-directive-scope)
1.=或者=attr 隔離作用域的屬性與父作用域的屬性進行雙向綁定,任何一方修改都會影響對方,此時指令中的屬性取值為controller中對應的$scope上屬性的值,這是最常用的方式;
2.@或者@attr 此時指令中的屬性取值為html中的字面量或者直接量。這樣是建立一個local scope property到DOM的property的綁定,因為值總是string類型,故這個值總是返回一個字符串,並且字符串的值永遠是從父作用域繼承而來的(即只能讀取父作用域中屬性值,不能修改,屬於單向綁定)。如果沒有通過@attr指定屬性名稱,那么本地名稱將於DOM的屬性一致。例如<widget my-attr=”hello {{name}}”>,widget的scope定義為:{localName:’@myAttr’}。那么,widget scope property的localName會映射出”hello {{name}}"轉換后的真實值。name屬性值改變后,widget scope的localName屬性也會相應地改變(僅僅單向,與上面的”=”不同)。name屬性是在父scope讀取的(不是組件scope).
3. & or &attr “Isolate”作用域把父作用域的屬性包裝成一個函數,從而以函數的方式讀寫父作用域的屬性,包裝方法是$parse();
controller
這是嵌套directive之間交互的重要屬性對象。
套用一個經典定義( what the O'Reily AngularJS book by the Google Team has to say):Controller - Create a controller which publishes an API for communicating across directives. A good example is Directive to Directive Communication。意即這個controller是用來存放一些可以在各個directive之間的共享的方法。當兩個或更多的directive之間需要通信時(即directive A需要用到directive B中的方法M),這時方法M就可以在directive B中的controller對象中來定義這個方法。此時方法M便類似一些公共的API(可以供其他的directive使用。)詳情請參考:http://www.cnblogs.com/xing901022/p/4290411.html
這個controller可以注入以下本地變量:
$scope (當前元素作用域),$element(當前元素),$attrs(當前元素的屬性對象),$transclude(后面介紹這個變量,感興趣的自己去谷歌)。
require
這個對象表示需要另外一個directive B(directive之間的通信)並且將會注入directive B所在的controller到linking function中,它的值一般是'xxxController'或者'^xxxController',表示這個directive需要使用directive B的controller屬性中的API。
restrict
限制directive在html中作用的方式:
'E' element name,表示以元素的形式在html中作用,例如<my-directive></my-directive>
'A' attribute ,表示以屬性的方式作用在html中,例如 <div my-directive='exp'></div>
'C' class ,表示以class的形式在html中作用,例如<div class='my-directive : exp ;'></div>
'M' 以注釋的方式在html中作用,例如<!-- directive: my-directive exp -->
當然,以上也可以組合使用,表示邏輯and。例如 restrict : 'EA',\
template
模板代替directive的元素的內容(默認),也可以完全代替元素本身(當replace值為true時有效),也可以用來封裝directive的元素的內容(當transclude的值為true時有效)。
取值:
- A string. For example
<div red-on-hover>{{delete_str}}</div>. - A function which takes two arguments
tElementandtAttrs(described in thecompilefunction api below) and returns a string value.
templateUrl
模板加載地址,異步加載。
replace
取值為true時,模板將會取代directive的元素;取值為false,模板將會取代directive的元素的內容。
transclude
一般情況下取值有三種true,'element',{...},通常用到前兩種。
當取值為true時, transclude the content (i.e. the child nodes) of the directive's element.舉個例子:
比如說你有一個申明transclude :true的directive叫做my-transclude-true,如下:
<div>
<my-transclude-true>
<span>{{ something }}</span>
{{ otherThing }}
</my-transclude-true>
</div>
當它被編譯之后,就會變成如下:
<div>
<my-transclude-true>
<!-- transcluded -->
</my-transclude-true>
</div>
my-transclude-true這個directive的內容content(子節點),即'<span>{{something..',將在這個directive中 可用。
再比如說你有一個申明transclude :'element'的directive叫做my-transclude-element,如下:
<div>
<my-transclude-element>
<span>{{ something }}</span>
{{ otherThing }}
</my-transclude-element>
</div>
被編譯之后,就會變成如下:
<div> <!-- transcluded --> </div>
這里,它的整個元素包括它的子節點都將在這個directive中可用。當transclude這個屬性被申明為'element'時,directive中的template屬性將會失去作用。
最后一個重要的屬性:link
這個屬性只有在compile這個屬性未定義時才能使用。
一般的語法糖:link : function (scope,iAttrs,iElement,controller){...}
link function的作用:Programmatically modify resulting DOM element instances, add event listeners, and set up data binding.以編程方式修改生成的DOM元素實例,添加事件監聽器,設置數據綁定。這是整個directive邏輯放的最多的地方。
下面說說這個函數的參數含義:
scope directive用來注冊監聽事件registering watches的作用域;
iElement 調用這個directive的實例的元素,比如:angular.module('app',[]).directive('myDirective',function(...){template :'<div data='data'><ul></ul></div>',replace:true});這個directive的元素就是'<div><ul></ul></div>',只有在postlink函數中處理元素的子節點'<ul></ul>'才是安全的,因為子節點已經被link了。(https://docs.angularjs.org/api/ng/service/$compile #link)
iAttrs 調用這個directive的實例的元素的屬性,如上為[data],是一個屬性列表集合。
controller:這個diective所需要的controller實例,通常在require這個屬性后面已經寫明。
以上大概就是平時開發過程中常用到的幾個屬性。
后面要續寫的內容預知:
1.嵌套directive之間的通訊(即directive之間的交互);
2.directive與controller之間的數據傳遞和通信;
3.controller與controller之間的數據傳遞和通信。
