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