AngularJS指令用法詳解
本文實例講述了AngularJS指令用法。分享給大家供大家參考,具體如下:
指令(directives)是任何AngularJS應用中最重要的成分。盡管AngularJS已經自帶了很多指令,你經常會發(fā)現需要自己親手創(chuàng)建一些特別的指令。本文將會帶你了解自定義指令并解釋如何在現實世界中的Angular項目中使用它們。文章的最后,我們將一起用Angular指令來創(chuàng)建一個簡單的筆記小應用。
綜述
一個指令就是一個引入新語法的東西。指令是在DOM元素上做的標記,并同時附加了一些特定的行為。例如,靜態(tài)的HTML并不知道如何來創(chuàng)建并顯示一個日期選擇插件。為了將這個新語法教給HTML我們需要一條指令。這個指令將會創(chuàng)建一個充當日期選擇器的元素。我們將在隨后看到如何實現這個指令。
如果你之前已經編寫過Angular應用,那么你已經使用過指令了,不管你有沒有意識到這點。你可能已經使用過像是ng-model,ng-repeat,ng-show等等這樣的指令。所有這些指令都將特定的功能綁定到了DOM元素之上。例如,ng-repeat會重復特定的元素,而ng-show會有條件的展示元素。如果你想要創(chuàng)建一個可拖動元素的話你可能需要創(chuàng)建一個指令。指令背后的基本思想很簡單。它通過在元素上綁定事件監(jiān)聽器并且將DOM變形來使HTML變得具有交互性。
從jQuery的角度來看指令
想想你如何使用jQuery來創(chuàng)建一個日期選擇器。我們首先在HTML中添加一個普通的input字段然后在jQuery中我們調用$(element).dataPicker()來將其轉換為一個日期選擇器。但是,考慮一下。當一個設計師想要來檢查這個標記時,他/她能夠立刻猜出這個字段究竟是干什么用的嗎?它僅僅是一個普通的input字段還是一個日期選擇器?你必須要查看jQuery來確認這點。Angular的方法是使用指令來擴展HTML。因此,一個日期選擇器的指令看上去可能如下所示:
<date-picker></date-picker>
或者如下所示:
<input type='text' data-picker/>
這種創(chuàng)建UI成分的方法既直觀又清楚。你可以看到元素就知道它的用途。
創(chuàng)建自定義指令
一個Angular指令可能以四種形式出現:
1.一個新的HTML元素(<date-picker></date-picker>)
2.一個元素上的屬性(<input type='text' date-picker/>)
3.作為一個類(<input type='text' class='date-picker'/>)
4.作為注釋(<!--directive:date-picker-->)
當然,我們完全可以決定我們的指令以什么形式出現在HTML中?,F在,我們來看看一個典型的Angular指令是如何寫成的。它和controller的注冊方式類似,但是它會返回一個簡單的對象(指令定義),其中那個包含有一些配置指令的屬性。下面的代碼展示了一個簡單和Hello World指令:
var app = angular.module('myapp',[]); app.directive('helloWorld',function(){ return { restrict: 'AE', replace: true, template: '<h3>Hello World!</h3>' } });
在上面的代碼中,app.diretive()函數在我們的模塊中注冊了一個新的指令。這個函數的第一個參數是指令的名稱。第二個參數是一個返回指令定義對象的函數。如果你的指令對額外的對象/服務(services)例如 $rootScope, $http 或者 $compile 有依賴,它們也可以在其中被注入。這個指令可以作為一個HTML元素來使用,如下所示:
<hello-world/>
或者:
<hello:world/>
或者作為一個屬性來使用:
<div hello-world></div>
或者:
<div hello:world/>
如果你想要兼容HTML5,你可以在屬性前面加上x-或者data-前綴。因此,下面的標記將會匹配helloWorld指令:
<div data‐hello‐world></div>
或者
<di vx‐hello‐world></div>
注意
當匹配指令時,Angular會從元素/屬性名之前去除前綴x-或者data-。然后將分隔符 - 或者 : 轉換為駝峰表示法已匹配注冊的指令。這就是為什么我們的helloWorld指令用在HTML中的時候實際上寫成了hello-world。
盡管上面的這個簡單的指令僅僅只是展示了一些靜態(tài)的文本,其中還是有一些值得我們去探究的有趣的點。我們已經在這個指令定義對象中使用了三個屬性。我們來看看這三個屬性分別都有什么用:
restrict - 這個屬性指明了一個指令應該如何在HTML中使用(記住指令可以以四種方式出現)。在這個例子中我們將它設置為'AE'。因此,這條指令可以作為一個HTML元素或者一個屬性來使用。為了允許指令作為一個類來使用我們可以將restrict設置為'AEC'。
template - 這個實行指明了當指令被Angular編譯和鏈接時生成的HTML標記。它不一定是一個簡單的字符串。template可以很復雜,其中經常會涉及其它的指令,表達式({{}}),等等。在大多數情況下你可能會想要使用templateUrl而不是template。因此,理想情況下你應該首先將模板放置在一個單獨的HTML文件中然后讓templateUrl指向它。
replace - 這個屬性指明了是否生成的模板會代替綁定指令的元素。在前面的例子中我們在HTML中使用指令為<hello-world></hello-world>,并將replace屬性設置為true。因此,在指令編譯后,生成的模板代替了<hello-world></hello-world>。最后的輸出結果是<h3>Hello World!</h3>。如果你將replace設置為false,默認情況下,輸出模板將會被插入到指令被調用的元素中。
link函數和作用域
有一個指令生成的模板是沒有用的除非它在正確的作用域中北編譯。默認情況下一個指令并不會得到一個新的子作用域。然而,它可以得到父作用域。這意味著如果一個指令位于在一個控制器中那么它將使用控制器的作用域。
為了利用作用域,我們可以使用一個叫做link的函數。它可以通過指令定義對象中的link屬性來配置。我們現在對helloworld指令做一些修改一遍當用戶在一個input字段中輸入一個顏色名稱時,Hello Wolld文字的背景顏色會自動發(fā)生改變。同樣,當一個用戶點擊Hello World文字時,背景顏色會重置為白色。相應的HTML標記如下所示:
<body ng-controller='MainCtrl'> <input type='text' ng-model='color' placeholder='Enter a color' / > <hello-wolrd/> </body>
修改后的helloWorld指令代碼如下所示:
app.directive('helloWorld',function(){ return { restrict: 'AE', replace: true, template: '<p style="background-color:{{color}}"></p>', link: function(scope,elem,attr){ 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 - 它代表指令被使用的作用域。在上面的例子中它等同于符控制器的作用域。
elem - 它代表綁定指令的元素的jQlite(jQuery的一個自己)包裹元素。如果你在AngularJS被包含之前就包括了jQuery,那么它將變成jQuery包裹元素。由于該元素已經被jQuery/jQlite包裹,我們沒有必要將它包含在$()中來進行DOM操作。
attars - 它代表綁定指令的元素上的屬性。例如,如果你在HTML元素上有一些指令形式為:<hello-world some-attribute></hello-world>,你可以在link函數內用attrs.someAttribute來引用這些屬性。
link函數主要是用來對DOM元素綁定事件監(jiān)聽器,監(jiān)視模型屬性變化,并更新DOM。在前面的指令代碼中,我們綁定了兩個監(jiān)聽器,click和mouseover。click處理函數重置了
的背景顏色,而mouseover處理函數則將游標改變?yōu)閜ointer。模板中擁有表達式{{color}},它將隨著父作用域中的模型color的變化而變化,從而改變了Hello World的背景色。
Compile函數
Compile函數主要用來在link函數運行之前進行一些DOM轉化。它接收下面幾個參數:
tElement - 指令綁定的元素
attrs - 元素上聲明的屬性
這里要注意compile不能夠訪問scope,而且必須返回一個link函數。但是,如果沒有compile函數以依然可以配置link函數。compile函數可以被寫成下面的樣子:
app.directive('test',function(){ return { compile: function(tElem,attrs){ //在這里原則性的做一些DOM轉換 return function(scope,elem,attrs){ //這里編寫link函數 } } } });
大多數時候,你僅僅只需要編寫link函數。這是因為大部分指令都只關心與注冊事件監(jiān)聽器,監(jiān)視器,更新DOM等等,它們在link函數中即可完成。像是ng-repeat這樣的指令,需要多次克隆并重復DOM元素,就需要在link函數運行之前使用compile函數。你可能會問威懾呢么要將兩個函數分別使用。為什么我們不能只編寫一個函數?為了回答這個問題我們需要理解Angular是如何編譯指令的!
指令是如何被編譯的
當應用在啟動時,Angular開始使用$compile服務解析DOM。這項服務會在標記中尋找指令然后將它們各自匹配到注冊的適齡。一旦所有的指令都已經被識別完成,Angular就開始執(zhí)行它們的compile函數。正如前面所提到的,compile函數返回一個link函數,該函數會被添加到稍后執(zhí)行的link函數隊列中。這叫做編譯階段(compile phase)。注意到即使同一個指令有幾個實例存在,compile函數也只會運行一次。
在編譯階段之后就到了鏈接階段(link phase),這時link函數就一個接一個的執(zhí)行。在這個階段中模板被生成,指令被運用到正確的作用域,DOM元素上開始有了事件監(jiān)聽器。不像是compile函數,lin函數會對每個指令的實例都執(zhí)行一次。
改變指令的作用域
默認情況下指令應該訪問父作用域。但是我們并不像對所有情況一概而論。如果我們對指令暴露了父控制器的scope,那么指令就可以自由的修改scope屬性。在一些情況下你的指令可能想要添加一些只有內部可以使用的屬性和函數。如果我們都在父作用域中完成,可能會污染了父作用域。因此,我們有兩種選擇:
一個子作用域 - 這個作用域會原型繼承父作用域。
一個隔離的作用域 - 一個全新的、不繼承、獨立存在的作用域。
作用域可以由指令定義對象中的scope屬性定義。下面的例子展示了這一點:
app.directive('helloWorld',function(){ return { scope: true, //使用一個繼承父作用域的自作用域 restrict: 'AE', replace: true, template: '<h3>Hello World!</h3>' } });
上面的代碼要求Angular為指令提供一個能夠原型繼承父作用域的子組用于。另一種情形,一個隔離作用域,代碼如下所示:
app.directive('helloWorld',function(){ return { scope: {}, //使用一個全新的隔離作用域 restrict: 'AE', replace: true, template: '<h3>Hello World!</h3>' } });
上面的指令使用一個不繼承父作用域的全新隔離作用域。當你想要創(chuàng)建一個可重用的組件時隔離作用域是一個很好的選擇。通過隔離作用域我們確保指令是自包含的兵可以輕松地插入到任何HTML app中。這種做法防止了父作用域被污染,由于它不可訪問父作用域。在我們修改后的helloWorld指令中如果你將scope設置為{},那么代碼就不會再正常運行。它將創(chuàng)建一個隔離的作用域然后表達式{{color}}將無法引用隔離作用域中的屬性因此值變?yōu)閡ndefined。
隔離作用域并不意味著你一點都不能獲取到父作用域中的屬性。有一些技巧可以使你訪問父作用域中的屬性同時監(jiān)聽這些屬性的變化。我們將在后續(xù)文章中提到這種高級技巧。
希望本文所述對大家AngularJS程序設計有所幫助。
相關文章
AngularJS操作鍵值對象類似java的hashmap(填坑小結)
我們知道java的hashmap中使用最多的是put(...),get(...)以及remove()方法,那么在angularJS中如何創(chuàng)造(使用)這樣一個對象呢?今天小編通過本文給大家分享下,感興趣的朋友一起學習吧2016-11-11