angular轉(zhuǎn)發(fā)

AngularJS是什么蝌戒?
AngularJs(后面就簡稱ng了)是一個(gè)用于設(shè)計(jì)動態(tài)web應(yīng)用的結(jié)構(gòu)框架召烂。首先碱工,它是一個(gè)框架,不是類庫,是像EXT一樣提供一整套方案用于設(shè)計(jì)web應(yīng)用怕篷。它不僅僅是一個(gè)JavaScript框架历筝,因?yàn)樗暮诵钠鋵?shí)是對HTML標(biāo)簽的增強(qiáng)。
何為HTML標(biāo)簽增強(qiáng)廊谓?其實(shí)就是使你能夠用標(biāo)簽完成一部分頁面邏輯漫谷,具體方式就是通過自定義標(biāo)簽、自定義屬性等蹂析,這些HTML原生沒有的標(biāo)簽/屬性在ng中有一個(gè)名字:指令(directive)。后面會詳細(xì)介紹碟婆。那么电抚,什么又是動態(tài)web應(yīng)用呢?與傳統(tǒng)web系統(tǒng)相區(qū)別竖共,web應(yīng)用能為用戶提供豐富的操作蝙叛,能夠隨用戶操作不斷更新視圖而不進(jìn)行url跳轉(zhuǎn)。ng官方也聲明它更適用于開發(fā)CRUD應(yīng)用公给,即數(shù)據(jù)操作比較多的應(yīng)用借帘,而非是游戲或圖像處理類應(yīng)用。
為了實(shí)現(xiàn)這些淌铐,ng引入了一些非常棒的特性肺然,包括模板機(jī)制、數(shù)據(jù)綁定腿准、模塊际起、指令、依賴注入吐葱、路由街望。通過數(shù)據(jù)與模板的綁定,能夠讓我們擺脫繁瑣的DOM操作弟跑,而將注意力集中在業(yè)務(wù)邏輯上灾前。
  另外一個(gè)疑問,ng是MVC框架嗎孟辑?還是MVVM框架哎甲?官網(wǎng)有提到ng的設(shè)計(jì)采用了MVC的基本思想,而又不完全是MVC扑浸,因?yàn)樵跁鴮懘a時(shí)我們確實(shí)是在用ng-controller這個(gè)指令(起碼從名字上看烧给,是MVC吧),但這個(gè)controller處理的業(yè)務(wù)基本上都是與view進(jìn)行交互喝噪,這么看來又很接近MVVM础嫡。讓我們把目光移到官網(wǎng)那個(gè)非醒目的title上:“AngularJS — Superheroic JavaScript MVW Framework”。
2.2 AngularJS簡單介紹
AngularJS 重新定義了前端應(yīng)用的開發(fā)方式。面對HTML和JavaScript之間的界線榴鼎,它
非但不畏縮不前伯诬,反而正面出擊,提出了有效的解決方案巫财。
很多前端應(yīng)用的開發(fā)框架盗似,比如Backbone、EmberJS等平项,都要求開發(fā)者繼承此框架特有的一些JavaScript對象赫舒。這種方式有其長處,但它不必要地污染了開發(fā)者自己代碼的對象空間闽瓢,還要求開發(fā)者去了解內(nèi)存里那些抽象對象接癌。盡管如此我們還是接受了這種方式,因?yàn)榫W(wǎng)絡(luò)最初的設(shè)計(jì)無法提供 我們今天所需的交互性扣讼,于是我們需要框架缺猛,來幫我們填補(bǔ)JavaScript和HTML之間的鴻溝。而且有了它椭符,你不用再“直接”操控DOM荔燎,只要給你的DOM注上metadata(即AngularJS里的directive們),然后讓AngularJS來幫你操縱DOM销钝。同時(shí)有咨,AngularJS不依賴(也不妨礙)任何其他的框架。你甚至可以基于其它的框架來開發(fā)AngularJS應(yīng)用曙搬。
API地址:http://docs.angularjs.org/api/摔吏;
AngularJS在github上的中文粗譯版地址:https://github.com/basestyle/angularjs-cn
2.3 什么時(shí)候該用AngularJS
AngularJS是一個(gè) MV* 框架纵装,最適于開發(fā)客戶端的單頁面應(yīng)用征讲。它不是個(gè)功能庫,而是用來開發(fā)動態(tài)網(wǎng)頁的框架橡娄。它專注于擴(kuò)展HTML的功能诗箍,提供動態(tài)數(shù)據(jù)綁定(data binding),而且它能跟其它框架(如jQuery)合作融洽挽唉。
如果你要開發(fā)的是單頁應(yīng)用滤祖,AngularJS就是你的上上之選。Gmail瓶籽、Google Docs匠童、Twitter和Facebook這樣的應(yīng)用,都很能發(fā)揮AngularJS的長處塑顺。但是像游戲開發(fā)之類對DOM進(jìn)行大量操縱汤求、又或者單純需要 極高運(yùn)行速度的應(yīng)用俏险,就不是AngularJS的用武之地了。
3 AugularJS特性
AngularJS是一個(gè)新出現(xiàn)的強(qiáng)大客戶端技術(shù)扬绪,提供給大家的一種開發(fā)強(qiáng)大應(yīng)用的方式竖独。這種方式利用并且擴(kuò)展HTML,CSS和javascript挤牛,并且彌補(bǔ)了它們的一些非常明顯的不足莹痢。本應(yīng)該使用HTML來實(shí)現(xiàn)而現(xiàn)在由它開發(fā)的動態(tài)一些內(nèi)容。
AngularJS有五個(gè)最重要的功能和特性:
3.1 特性一:雙向的數(shù)據(jù)綁定
數(shù)據(jù)綁定可能是AngularJS最酷最實(shí)用的特性墓赴。它能夠幫助你避免書寫大量的初始代碼從而節(jié)約開發(fā)時(shí)間竞膳。一個(gè)典型的web應(yīng)用可能包含了80%的代碼用來處理,查詢和監(jiān)聽DOM诫硕。數(shù)據(jù)綁定是的代碼更少顶猜,你可以專注于你的應(yīng)用。
我們想象一下Model是你的應(yīng)用中的簡單事實(shí)痘括。你的Model是你用來讀取或者更新的部分。數(shù)據(jù)綁定指令提供了你的Model投射到view的方法滔吠。這些投射可以無縫的纲菌,毫不影響的應(yīng)用到web應(yīng)用中。
傳統(tǒng)來說疮绷,當(dāng)model變化了翰舌。 開發(fā)人員需要手動處理DOM元素并且將屬性反映到這些變化中。這個(gè)一個(gè)雙向的過程冬骚。一方面椅贱,model變化驅(qū)動了DOM中元素變化,另一方面只冻,DOM元素的變化也會影響到Model庇麦。這個(gè)在用戶互動中更加復(fù)雜,因?yàn)殚_發(fā)人員需要處理和解析
這些互動喜德,然后融合到一個(gè)model中山橄,并且更新View。這是一個(gè)手動的復(fù)雜過程舍悯,當(dāng)一個(gè)應(yīng)用非常龐大的時(shí)候呛讲,將會是一件非常費(fèi)勁的事情效斑。
這里肯定有更好的解決方案!那就是AngularJS的雙向數(shù)據(jù)綁定,能夠同步DOM和Model等等往产。
這里有一個(gè)非常簡單的例子,用來演示一個(gè)input輸入框和<h1>元素的雙向綁定(例01):

說明:實(shí)際效果請大家看AngularJS/demo/index.html
3.2 特性二:模板
在AngularJS中硕并,一個(gè)模板就是一個(gè)HTML文件霎褐。但是HTML的內(nèi)容擴(kuò)展了,包含了很多幫助你映射model到view的內(nèi)容。
HTML模板將會被瀏覽器解析到DOM中呵晚。DOM然后成為AngularJS編譯器的輸入蜘腌。AngularJS將會遍歷DOM模板來生成一些指導(dǎo),即饵隙,directive(指令)撮珠。所有的指令都負(fù)責(zé)針對view來設(shè)置數(shù)據(jù)綁定。
我們要理解AuguarJS并不把模板當(dāng)做String來操作金矛。輸入AngularJS的是DOM而非string芯急。數(shù)據(jù)綁定是DOM變化,不是字符串的連接或者innerHTML變化驶俊。使用DOM作為輸入娶耍,而不是字符串,是AngularJS區(qū)別于其它的框架的最大原因饼酿。使用DOM允許你擴(kuò)展指令詞匯并且可以創(chuàng)建你自己的指令榕酒,甚至開發(fā)可重用的組件。
最大的好處是為設(shè)計(jì)師和開發(fā)者創(chuàng)建了一個(gè)緊密的工作流故俐。設(shè)計(jì)師可以像往常一樣開發(fā)標(biāo)簽想鹰,然后開發(fā)者拿過來添加上功能,通過數(shù)據(jù)綁定將會使得這個(gè)過程非常簡單药版。
這里有一個(gè)例子辑舷,我們使用ng-repeat指令來循環(huán)圖片數(shù)組并且加入img模板,如下:
function AlbumCtrl($scope) {
scope.images = [
{"image":"img/image_01.png", "description":"Image 01 description"},
{"image":"img/image_02.png", "description":"Image 02 description"},
{"image":"img/image_03.png", "description":"Image 03 description"},
{"image":"img/image_04.png", "description":"Image 04 description"},
{"image":"img/image_05.png", "description":"Image 05 description"}
];
}
<div ng-controller="AlbumCtrl">
<ul>
<li ng-repeat="image in images">


</li>
</ul>
</div>
這里還有一件事值得提一句槽片,AngularJS并不強(qiáng)制你學(xué)習(xí)一個(gè)新的語法或者從你的應(yīng)用中提出你的模板何缓。
3.3 特性三:MVC
針對客戶端應(yīng)用開發(fā)AngularJS吸收了傳統(tǒng)的MVC基本原則。MVC或者M(jìn)odel-View-Controll設(shè)計(jì)模式針對不同的人可能意味不同的東西还栓。AngularJS并不執(zhí)行傳統(tǒng)意義上的MVC碌廓,更接近于MVVM(Moodel-View-ViewModel)。
Model
model是應(yīng)用中的簡單數(shù)據(jù)剩盒。一般是簡單的javascript對象氓皱。這里沒有必要繼承框架的classes,使用proxy對象封裝或者使用特別的setter/getter方法來訪問勃刨。事實(shí)上我們處理vanilla javascript的方法就是一個(gè)非常好的特性波材,這種方法使得我們更少使用應(yīng)用的原型。
ViewModel
viewmodel是一個(gè)用來提供特別數(shù)據(jù)和方法從而維護(hù)指定view的對象身隐。
viewmodel是$scope的對象廷区,只存在于AnguarJS的應(yīng)用中。$scope只是一個(gè)簡單的js對象贾铝,這個(gè)對象使用簡單的API來偵測和廣播狀態(tài)變化隙轻。
Controller
controller負(fù)責(zé)設(shè)置初始狀態(tài)和參數(shù)化$scope方法用以控制行為埠帕。需要指出的controller并不保存狀態(tài)也不和遠(yuǎn)程服務(wù)互動。
View
view是AngularJS解析后渲染和綁定后生成的HTML 玖绿。這個(gè)部分幫助你創(chuàng)建web應(yīng)用的架構(gòu)敛瓷。$scope擁有一個(gè)針對數(shù)據(jù)的參考,controller定義行為斑匪,view處理布局和互動呐籽。
3.4 特性四:服務(wù)和依賴注入
AngularJS服務(wù)其作用就是對外提供某個(gè)特定的功能。
AngularJS擁有內(nèi)建的依賴注入(DI)子系統(tǒng)蚀瘸,可以幫助開發(fā)人員更容易的開發(fā)狡蝶,理解和測試應(yīng)用。
DI允許你請求你的依賴贮勃,而不是自己找尋它們贪惹。比如,我們需要一個(gè)東西寂嘉,DI負(fù)責(zé)找創(chuàng)建并且提供給我們奏瞬。
為了而得到核心的AngularJS服務(wù),只需要添加一個(gè)簡單服務(wù)作為參數(shù)泉孩,AngularJS會偵測并且提供給你:
function EditCtrl($scope, $location, $routeParams) {
// Something clever here...
}
你也可以定義自己的服務(wù)并且讓它們注入:
angular.module('MyServiceModule', []).
factory('notify', ['$window', function (win) {
return function (msg) {
win.alert(msg);
};
}]);
function myController(scope, notifyService) {
scope.callNotify = function (msg) {
notifyService(msg);
};
}
myController.$inject = ['$scope', 'notify'];
3.5 特性五:指令(Directives)
指令是我個(gè)人最喜歡的特性丝格。你是不是也希望瀏覽器可以做點(diǎn)兒有意思的事情?那么AngularJS可以做到棵譬。
指令可以用來創(chuàng)建自定義的標(biāo)簽。它們可以用來裝飾元素或者操作DOM屬性预伺《┫蹋可以作為標(biāo)簽、屬性酬诀、注釋和類名使用脏嚷。
這里是一個(gè)例子,它監(jiān)聽一個(gè)事件并且針對的更新它的$scope 瞒御,如下:
myModule.directive('myComponent', function(mySharedService) {
return {
restrict: 'E',
controller: function($scope, $attrs, mySharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'Directive: ' + mySharedService.message;
});
},
replace: true,
template: '<input>'
};
});
然后父叙,你可以使用這個(gè)自定義的directive來使用:
<my-component ng-model="message"></my-component>
使用一系列的組件來創(chuàng)建你自己的應(yīng)用將會讓你更方便的添加,刪除和更新功能肴裙。
4 功能介紹
4.1數(shù)據(jù)綁定
AngularJS的雙向數(shù)據(jù)綁定趾唱,意味著你可以在Mode(JS)中改變數(shù)據(jù),而這些變動立刻就會自動出現(xiàn)在View上蜻懦,反之亦然甜癞。即:一方面可以做到model變化驅(qū)動了DOM中元素變化,另一方面也可以做到DOM元素的變化也會影響到Model宛乃。
在我們使用jQuery的時(shí)候悠咱,代碼中會大量充斥類似這樣的語句:var val = $(‘#id’).val(); $(‘#id’).html(str);等等蒸辆,即頻繁的DOM操作(讀取和寫入),其實(shí)我們的最終目的并不是要操作DOM析既,而是要實(shí)現(xiàn)業(yè)務(wù)邏輯躬贡。ng的綁定將讓你擺脫DOM操作,只要模板與數(shù)據(jù)通過聲明進(jìn)行了綁定眼坏,兩者將隨時(shí)保持同步拂玻,最新的數(shù)據(jù)會實(shí)時(shí)顯示在頁面中,頁面中用戶修改的數(shù)據(jù)也會實(shí)時(shí)被記錄在數(shù)據(jù)模型中空骚。
從View到Controller再到View的數(shù)據(jù)交互(例01):
<html ng-app="demoApp">
……
<input type="text" ng-model="user.name" placeholder="請輸入名稱"/>
Hello, {{ user.name }}纺讲!
……
關(guān)鍵: ng-app 、 ng-model 和 { {user.name } }
首先: <html>元素的ng-app屬性囤屹。標(biāo)識這個(gè)DOM里面的內(nèi)容將啟用AngularJS應(yīng)用熬甚。
其次:告訴AngularJS,對頁面上的“user.name” 這個(gè)Model進(jìn)行雙向數(shù)據(jù)綁定肋坚。
第三:告訴AngularJS乡括,在“{{ user.name}}”這個(gè)指令模版上顯示“user.name”這個(gè)Model的數(shù)據(jù)。
從Server到Controller再到View的數(shù)據(jù)交互(例02):
<html ng-app="demoApp">
……
<div ng-controller="demoController">
<input type="text" ng-model="user.name" disabled="disabled"/>
<a href="javascript:void(0);" target="_blank" rel="nofollow">獲取名字</a>
……
demoApp.controller("demoController", function($http, $scope){
$scope. getAjaxUser = function(){
// $http.get({url:"../xxx.action"}).success(function(data){
// $scope.user= data;
// });
$scope.user = {"name":"從JOSN中獲取的名稱","age":22};
};
});
改變$scope中的user智厌,View也會自動更新诲泌。
4.2 scopes、module铣鹏、controller
4.2.1 scopes
$scope是一個(gè)把view(一個(gè)DOM元素)連結(jié)到controller上的對象敷扫。在我們的MVC結(jié)構(gòu)里,這個(gè) $scope 將成為model诚卸,它提供一個(gè)綁定到DOM元素(以及其子元素)上的excecution context葵第。
盡管聽起來有點(diǎn)復(fù)雜,但 $scope 實(shí)際上就是一個(gè)JavaScript對象合溺,controller和view都可以訪問它卒密,所以我們可以利用它在兩者間傳遞信息。在這個(gè) $scope 對象里棠赛,我們既存儲數(shù)據(jù)哮奇,又存儲將要運(yùn)行在view上的函數(shù)。
每一個(gè)Angular應(yīng)用都會有一個(gè) $rootScope睛约。這個(gè) $rootScope 是最頂級的scope鼎俘,它對應(yīng)著含有 ng-app 指令屬性的那個(gè)DOM元素。
app.run(function($rootScope) { $rootScope.name = "張三"; });
如果頁面上沒有明確設(shè)定 $scope 辩涝,Angular 就會把數(shù)據(jù)和函數(shù)都綁定到這里而芥, 第一部分中的例子就是靠這一點(diǎn)成功運(yùn)行的。
這樣膀值,我們就可以在view的任何地方訪問這個(gè)name屬性棍丐,使用模版表達(dá)式{{}}误辑,像這樣:
{{ name }}
4.2.2 module
首先需要明確一下模板的概念。在我還不知道有模板這個(gè)東西的時(shí)候歌逢,曾經(jīng)用js拼接出很長的HTML字符串巾钉,然后append到頁面中,這種方式想想真是又土又笨秘案。后來又看到可以把HTML代碼包裹在一個(gè)<script>標(biāo)簽中當(dāng)作模板砰苍,然后按需要取來使用。
在ng中阱高,模板十分簡單赚导,它就是我們頁面上的HTML代碼,不需要附加任何額外的東西赤惊。在模板中可以使用各種指令來增強(qiáng)它的功能吼旧,這些指令可以讓你把模板和數(shù)據(jù)巧妙的綁定起來。
在<html>標(biāo)簽上多了一個(gè)屬性ng-app=”MyApp”未舟,它的作用就是用來指定ng的作用域是在<html>標(biāo)簽以內(nèi)部分圈暗。在js中,我們調(diào)用angular對象的module方法來聲明一個(gè)模塊裕膀,模塊的名字和ng-app的值對應(yīng)员串。這樣聲明一下就可以讓ng運(yùn)行起來了。
示例:
<html ng-app="demoApp">
var demoApp = angular.module('demoApp', []);
4.2.3 ng-controller
要明確創(chuàng)建一個(gè)$scope 對象昼扛,我們就要給DOM元素安上一個(gè)controller對象寸齐,使用的是ng-controller 指令屬性:
<div ng-controller="MyController"> {{ person.name }} </div>
ng-controller指令給所在的DOM元素創(chuàng)建了一個(gè)新的$scope 對象,并將這個(gè)$scope 對象包含進(jìn)外層DOM元素的$scope 對象里抄谐。在上面的例子里渺鹦,這個(gè)外層DOM元素的$scope 對象,就是$rootScope 對象斯稳。這個(gè)scope鏈?zhǔn)沁@樣的:

所有scope都遵循原型繼承(prototypal inheritance),這意味著它們都能訪問父scope們迹恐。對任何屬性和方法挣惰,如果AngularJS在當(dāng)前scope上找不到,就會到父 scope上去找殴边,如果在父scope上也沒找到憎茂,就會繼續(xù)向上回溯,一直到$rootScope 上锤岸。即如果controller是多層嵌套的竖幔,就會從最里面一直往外找,這個(gè)scope鏈?zhǔn)沁@樣的:

唯一的例外:有些指令屬性可以選擇性地創(chuàng)建一個(gè)獨(dú)立的scope是偷,讓這個(gè)scope不繼承它的父scope們拳氢,這個(gè)會在指令詳解中說明募逞。
4.3 ajax
$http 服務(wù)是AngularJS的核心服務(wù)之一,它幫助我們通過XMLHttpRequest對象或JSONP與遠(yuǎn)程HTTP服務(wù)進(jìn)行交流馋评。
$http 服務(wù)是這樣一個(gè)函數(shù):它接受一個(gè)設(shè)置對象放接,其中指定了如何創(chuàng)建HTTP請求;它將返回一個(gè)承諾(*參考JavaScript異步編程的promise模式)留特,其中提供兩個(gè)方法: success方法和error方法纠脾。
demoApp.controller("demoController", function($http, $scope){
$scope. getAjaxUser = function(){
$http.get({url:"../xxx.action"}).success(function(data){
alert(data);
}).error(function(){
Alert(“出錯(cuò)了!”);
});

};
});
AngularJS的AJAX與jquery等框架的AJAX基本一致蜕青,這里就不多說了苟蹈。
4.4表達(dá)式
ng中的表達(dá)式與javascript表達(dá)式類似但是不可以劃等號,它是ng自己定義的一套模式右核。表達(dá)式可以作為指令的值慧脱,如ng-modle=”people.name”、ng-click=”showMe()”蒙兰,看起來是如此像字符串磷瘤,故而也叫字符串表達(dá)式。也可以在標(biāo)記中使用表達(dá)式搜变,如{{1+2}}采缚,或者與過濾器一起使用{{1+2 | currency}}。在框架內(nèi)部挠他,字符串不會簡單的使用eval()來執(zhí)行扳抽,而是有一個(gè)專門的$parse服務(wù)來處理。在ng表達(dá)式中不可以使用循環(huán)語句殖侵、判斷語句贸呢,事實(shí)上在模板中使用復(fù)雜的表達(dá)式也是一個(gè)不推薦的做法,這樣視圖與邏輯就混雜在一起了
我們在使用其他模板庫時(shí)拢军,一般都會有模板的循環(huán)輸出楞陷、分支輸出、邏輯判斷等類似的控制茉唉。
要想理解指令屬性的運(yùn)作固蛾,我們必須先理解表達(dá)式。在之前的例子里我們已經(jīng)見過表達(dá)式度陆,例如 {{ user.name }}艾凯。
請查看例03、例04懂傀、例05趾诗。
{{ 8 + 1 }} 9
{{ person }} {"name":"Ari Lerner"}
{{ 10 * 3.3 | currency }} $33.00
表達(dá)式粗略來看有點(diǎn)像 eval(javascript) 的結(jié)果。它們會經(jīng)過Angular.js的處理蹬蚁,從而擁有以下重要而獨(dú)特的性質(zhì):
l 所有表達(dá)式都在scope這個(gè)context里被執(zhí)行恃泪,因此可以使用所有本地 $scope 中的變量郑兴。
l 如果一個(gè)表達(dá)式的執(zhí)行導(dǎo)致類型錯(cuò)誤或引用錯(cuò)誤,這些錯(cuò)誤將不會被拋出悟泵。
l 表達(dá)式里不允許任何控制函數(shù)流程的功能(如if/else等條件語句)
l 表達(dá)式可接受一個(gè)或多個(gè)串聯(lián)起來的過濾器杈笔。
4.5過濾器
過濾器(filter)正如其名,作用就是接收一個(gè)輸入糕非,通過某個(gè)規(guī)則進(jìn)行處理蒙具,然后返回處理后的結(jié)果。主要用在數(shù)據(jù)的格式化上朽肥,例如獲取一個(gè)數(shù)組中的子集禁筏,對數(shù)組中的元素進(jìn)行排序等。過濾器通常是伴隨標(biāo)記來使用的衡招,將你model中的數(shù)據(jù)格式化為需要的格式篱昔。表單的控制功能主要涉及到數(shù)據(jù)驗(yàn)證以及表單控件的增強(qiáng)。ng內(nèi)置了一些過濾器始腾,它們是:
currency(貨幣)州刽、date(日期)、filter(子串匹配)浪箭、json(格式化json對象)穗椅、limitTo(限制個(gè)數(shù))、lowercase(小寫)奶栖、uppercase(大寫)匹表、number(數(shù)字)、orderBy(排序)宣鄙。
4.5.1過濾器使用方式
總共九種袍镀。除此之外還可以自定義過濾器,這個(gè)就強(qiáng)大了冻晤,可以滿足任何要求的數(shù)據(jù)處理苇羡。Filter還是很簡單的,需要明白的是內(nèi)置的filter如何使用鼻弧,以及自己如何定義一個(gè)filter设江。
filter的兩種使用方法:
  1. 在模板中使用filter
  我們可以直接在{{}}中使用filter,跟在表達(dá)式后面用 | 分割温数,語法如下:
{{ expression | filter }}
也可以多個(gè)filter連用绣硝,上一個(gè)filter的輸出將作為下一個(gè)filter的輸入:
{{ expression | filter1 | filter2 | ... }}  
filter可以接收參數(shù)蜻势,參數(shù)用 : 進(jìn)行分割撑刺,如下:
{{ expression | filter:argument1:argument2:... }}  
除了對{{}}中的數(shù)據(jù)進(jìn)行格式化,我們還可以在指令中使用filter握玛,例如先對數(shù)組array進(jìn)行過濾處理够傍,然后再循環(huán)輸出:
<span ng-repeat="a in array | filter ">

  1. 在controller和service中使用filter
      我們的js代碼中也可以使用過濾器甫菠,方式就是我們熟悉的依賴注入瞭空,例如我要在controller中使用currency過濾器商源,只需將它注入到該controller中即可,代碼如下:
    app.controller('testC',function($scope,currencyFilter){
    $scope.num = currencyFilter(123534);
    }  
    在模板中使用{{num}}就可以直接輸出$123,534.00了谭羔!在服務(wù)中使用filter也是同樣的道理安聘。
      如果你要在controller中使用多個(gè)filter痰洒,并不需要一個(gè)一個(gè)注入嗎,ng提供了一個(gè)$filter服務(wù)可以來調(diào)用所需的filter浴韭,你只需注入一個(gè)$filter就夠了丘喻,使用方法如下:
    app.controller('testC',function($scope,$filter){
    $scope.num = $filter('currency')(123534);  
    $scope.date = $filter('date')(new Date());
    }  
    可以達(dá)到同樣的效果。好處是你可以方便使用不同的filter了念颈。
    4.5.2 ng的內(nèi)置過濾器
    ng內(nèi)置了九種過濾器泉粉,使用方法都非常簡單,看文檔即懂榴芳。不過為了以后不去翻它的文檔嗡靡,我在這里還是做一個(gè)詳細(xì)的記錄。
    currency(貨幣)窟感、date(日期)讨彼、filter(子串匹配)、json(格式化json對象)肌括、limitTo(限制個(gè)數(shù))点骑、lowercase(小寫)、uppercase(大寫)谍夭、number(數(shù)字)黑滴、orderBy(排序)
  2. currency (貨幣處理)
      使用currency可以將數(shù)字格式化為貨幣,默認(rèn)是美元符號紧索,你可以自己傳入所需的符號袁辈,例如我傳入人民幣:
    {{num | currency : '¥'}}
  3. date (日期格式化)
      原生的js對日期的格式化能力有限,ng提供的date過濾器基本可以滿足一般的格式化要求珠漂。用法如下:
    {{date | date : 'yyyy-MM-dd hh:mm:ss EEEE'}}  
    參數(shù)用來指定所要的格式晚缩,y M d h m s E 分別表示 年 月 日 時(shí) 分 秒 星期,你可以自由組合它們媳危。也可以使用不同的個(gè)數(shù)來限制格式化的位數(shù)荞彼。另外參數(shù)也可以使用特定的描述性字符串,例如“shortTime”將會把時(shí)間格式為12:05 pm這樣的待笑。ng提供了八種描述性的字符串鸣皂,個(gè)人覺得這些有點(diǎn)多余,我完全可以根據(jù)自己的意愿組合出想要的格式,不愿意去記這么多單詞~
  4. filter(匹配子串)
      這個(gè)名叫filter的filter寞缝。用來處理一個(gè)數(shù)組癌压,然后可以過濾出含有某個(gè)子串的元素,作為一個(gè)子數(shù)組來返回荆陆√步欤可以是字符串?dāng)?shù)組,也可以是對象數(shù)組被啼。如果是對象數(shù)組帜消,可以匹配屬性的值。它接收一個(gè)參數(shù)浓体,用來定義子串的匹配規(guī)則券犁。下面舉個(gè)例子說明一下參數(shù)的用法,我用現(xiàn)在特別火的幾個(gè)孩子定義了一個(gè)數(shù)組:
    $scope.childrenArray = [
    {name:'kimi',age:3},
    {name:'cindy',age:4},
    {name:'anglar',age:4},
    {name:'shitou',age:6},
    {name:'tiantian',age:5}
    ];
    $scope.func = function(e){return e.age>4;}{{ childrenArray | filter : 'a' }} //匹配屬性值中含有a的
    {{ childrenArray | filter : 4 }} //匹配屬性值中含有4的
    {{ childrenArray | filter : {name : 'i'} }} //參數(shù)是對象汹碱,匹配name屬性中含有i的
    {{childrenArray | filter : func }} //參數(shù)是函數(shù)粘衬,指定返回age>4的
  5. json(格式化json對象)
      json過濾器可以把一個(gè)js對象格式化為json字符串,沒有參數(shù)咳促。這東西有什么用呢稚新,我一般也不會在頁面上輸出一個(gè)json串啊,官網(wǎng)說它可以用來進(jìn)行調(diào)試跪腹,嗯褂删,是個(gè)不錯(cuò)的選擇〕迦祝或者屯阀,也可以用在js中使用,作用就和我們熟悉的JSON.stringify()一樣轴术。用法超級簡單:
    {{ jsonTest | json}}
  6. limitTo(限制數(shù)組長度或字符串長度)
      limitTo過濾器用來截取數(shù)組或字符串难衰,接收一個(gè)參數(shù)用來指定截取的長度,如果參數(shù)是負(fù)值逗栽,則從數(shù)組尾部開始截取盖袭。個(gè)人覺得這個(gè)filter有點(diǎn)雞肋,首先只能從數(shù)組或字符串的開頭/尾部進(jìn)行截取彼宠,其次鳄虱,js原生的函數(shù)就可以代替它了,看看怎么用吧:
    {{ childrenArray | limitTo : 2 }} //將會顯示數(shù)組中的前兩項(xiàng)
  7. lowercase(小寫)
      把數(shù)據(jù)轉(zhuǎn)化為全部小寫凭峡。太簡單了拙已,不多解釋。同樣是很雞肋的一個(gè)filter摧冀,沒有參數(shù)倍踪,只能把整個(gè)字符串變?yōu)樾懨拐牵荒苤付ㄗ帜浮T趺从梦叶紤械脤懥恕?/li>
  8. uppercase(大寫)
      同上惭适。
  9. number(格式化數(shù)字)
      number過濾器可以為一個(gè)數(shù)字加上千位分割,像這樣楼镐,123,456,789癞志。同時(shí)接收一個(gè)參數(shù),可以指定float類型保留幾位小數(shù):
    {{ num | number : 2 }}
  10. orderBy(排序)
      orderBy過濾器可以將一個(gè)數(shù)組中的元素進(jìn)行排序框产,接收一個(gè)參數(shù)來指定排序規(guī)則凄杯,參數(shù)可以是一個(gè)字符串,表示以該屬性名稱進(jìn)行排序秉宿〗渫唬可以是一個(gè)函數(shù),定義排序?qū)傩悦枘馈_€可以是一個(gè)數(shù)組膊存,表示依次按數(shù)組中的屬性值進(jìn)行排序(若按第一項(xiàng)比較的值相等,再按第二項(xiàng)比較)忱叭,還是拿上面的孩子數(shù)組舉例:
    <div>{{ childrenArray | orderBy : 'age' }}</div> //按age屬性值進(jìn)行排序隔崎,若是-age,則倒序
    <div>{{ childrenArray | orderBy : orderFunc }}</div> //按照函數(shù)的返回值進(jìn)行排序
    <div>{{ childrenArray | orderBy : ['age','name'] }}</div> //如果age相同韵丑,按照name進(jìn)行排序  內(nèi)置的過濾器介紹完了爵卒,寫的我都快睡著了。撵彻。钓株。正如你所看到的,ng內(nèi)置的過濾器也并不是萬能的陌僵,事實(shí)上好多都比較雞肋轴合。更個(gè)性化的需求就需要我們來定義自己的過濾器了,下面來看看如何自定義過濾器碗短。
    4.5.3自定義過濾器及示例
      filter的自定義方式也很簡單值桩,使用module的filter方法,返回一個(gè)函數(shù)豪椿,該函數(shù)接收
    輸入值奔坟,并返回處理后的結(jié)果。話不多說搭盾,我們來寫一個(gè)看看咳秉。比如我需要一個(gè)過濾器,它可以返回一個(gè)數(shù)組中下標(biāo)為奇數(shù)的元素鸯隅,代碼如下:
    app.filter('odditems',function(){
    return function(inputArray){
    var array = [];
    for(var i=0;i<inputArray.length;i++){
    if(i%2!==0){
    array.push(inputArray[i]);
    }
    }
    return array;
    }
    });  
    格式就是這樣澜建,你的處理邏輯就寫在內(nèi)部的那個(gè)閉包函數(shù)中向挖。你也可以讓自己的過濾器接收參數(shù),參數(shù)就定義在return的那個(gè)函數(shù)中炕舵,作為第二個(gè)參數(shù)何之,或者更多個(gè)參數(shù)也可以。
    自定義過濾器實(shí)例(例04):
    /* View html /
    First name:<input ng-model="user.firstName"/>

    Last name:<input ng-model="user.lastName"/>

    First name:{{user.firstName}} Last name:{{user.lastName}}

    Fullname:{{user | flFullname}}

    Fullname:{{user | flFullname:"-"}}

    Fullname:{{user | flFullname:"?" | uppercase }}
    /
    Controller js */
    demoApp.filter("flFullname", function() {
    return function(user, sep) {
    sep = sep || " ";
    user = user || {};
    fullName = "";
    if(user.firstName){fullName += user.firstName;}
    if(user.lastName){fullName = fullName + sep + user.lastName;}
    if(fullName && fullName.length>0){return fullName;
    }else{return "";}
    };
    });
    4.6指令(directive)
      通過使用模板咽筋,我們可以把model和controller中的數(shù)據(jù)組裝起來呈現(xiàn)給瀏覽器溶推,還可以通過數(shù)據(jù)綁定,實(shí)時(shí)更新視圖奸攻,讓我們的頁面變成動態(tài)的蒜危。
      模板中可以使用的東西包括以下四種:
    1.指令(directive):ng提供的或者自定義的標(biāo)簽和屬性,用來增強(qiáng)HTML表現(xiàn)力睹耐;
    2.標(biāo)記(markup):即雙大括號{{}}辐赞,可將數(shù)據(jù)單向綁定到HTML中;
    3.過濾器(filter):用來格式化輸出數(shù)據(jù)硝训;
    4.表單控制:用來增強(qiáng)表單的驗(yàn)證功能响委。
    其中,指令無疑是使用量最大的窖梁,ng內(nèi)置了很多指令用來控制模板晃酒,如ng-repeat,ng-class窄绒,也有很多指令來幫你完成業(yè)務(wù)邏輯贝次,如ng-controller,ng-model。
    指令的幾種使用方式如下:
    l 作為標(biāo)簽:<my-dir></my-dir>
    l 作為屬性:<span my-dir="exp"></span>
    l 作為注釋:
    l 作為類名:<span class="my-dir: exp;"></span>
    其實(shí)常用的就是作為標(biāo)簽和屬性彰导。
    4.6.1樣式相關(guān)的指令
      既然模板就是普通的HTML蛔翅,那我首要關(guān)心的就是樣式的控制,元素的定位位谋、字體山析、背景色等等如何可以靈活控制。下面來看看常用的樣式控制指令掏父。
  11. ng-class
       ng-class用來給元素綁定類名笋轨,其表達(dá)式的返回值可以是以下三種:
    l 類名字符串,可以用空格分割多個(gè)類名赊淑,如’redtext boldtext’爵政;
    l 類名數(shù)組,數(shù)組中的每一項(xiàng)都會層疊起來生效陶缺;
    l 一個(gè)名值對應(yīng)的map钾挟,其鍵值為類名,值為boolean類型饱岸,當(dāng)值為true時(shí)掺出,該類會被加在元素上徽千。
      下面來看一個(gè)使用map的例子:
    ng-class測試
    紅色 加粗 刪除線
    map:{redtext:{{red}}, boldtext:{{bold}}, striketext:{{strike}}}
      如果你想拼接一個(gè)類名出來,可以使用插值表達(dá)式汤锨,如:
      <div class=”{{style}}text”>字體樣式測試</div>
      然后在controller中指定style的值:
      $scope.style = ‘red’;
      注意我用了class而不是ng-class双抽,這是不可以對換的,官方的文檔也未做說明闲礼,姑且認(rèn)為這是ng的語法規(guī)則吧牍汹。
      與ng-class相近的,ng還提供了ng-class-odd位仁、ng-class-even兩個(gè)指令,用來配合ng-repeat分別在奇數(shù)列和偶數(shù)列使用對應(yīng)的類方椎。這個(gè)用來在表格中實(shí)現(xiàn)隔行換色再方便不過了聂抢。
  12. ng-style
      ng-style用來綁定元素的css樣式,其表達(dá)式的返回值為一個(gè)js對象棠众,鍵為css樣式名琳疏,值為該樣式對應(yīng)的合法取值。用法比較簡單:
    <div ng-style="{color:'red'}">ng-style測試</div>
    <div ng-style="style">ng-style測試</div>
    $scope.style = {color:'red'};
  13. ng-show闸拿,ng-hide
       對于比較常用的元素顯隱控制空盼,ng也做了封裝,ng-show和ng-hide的值為boolean類型的表達(dá)式新荤,當(dāng)值為true時(shí)揽趾,對應(yīng)的show或hide生效】凉牵框架會用display:block和display:none來控制元素的顯隱篱瞎。
    4.6.2表單控件功能相關(guān)指令
      對于常用的表單控件功能,ng也做了封裝痒芝,方便靈活控制俐筋。
      ng-checked控制radio和checkbox的選中狀態(tài)
      ng-selected控制下拉框的選中狀態(tài)
      ng-disabled控制失效狀態(tài)
      ng-multiple控制多選
      ng-readonly控制只讀狀態(tài)
      以上指令的取值均為boolean類型,當(dāng)值為true時(shí)相關(guān)狀態(tài)生效严衬,道理比較簡單就不多做解釋澄者。注意: 上面的這些只是單向綁定,即只是從數(shù)據(jù)到模板请琳,不能反作用于數(shù)據(jù)粱挡。要雙向綁定,還是要使用 ng-model 俄精。
    4.6.3事件綁定相關(guān)指令
    事件綁定是javascrpt中比較重要的一部分內(nèi)容抱怔,ng對此也做了詳細(xì)的封裝,正如我們之前使用過的ng-click一樣嘀倒,事件的指令如下:
    ng-click
      ng-change
      ng-dblclick
      ng-mousedown
      ng-mouseenter
      ng-mouseleave
      ng-mousemove
      ng-mouseover
      ng-mouseup
      ng-submit
      事件綁定指令的取值為函數(shù)屈留,并且需要加上括號局冰,例如:
    <select ng-change=”change($event)”></select>  
    然后在controller中定義如下:
    $scope.change = function($event){
    alert($event.target);
    //……………………
    }  
    在模板中可以用變量$event將事件對象傳遞到controller中。
    對于ng的這種設(shè)計(jì)灌危,一些人有所質(zhì)疑康二,視圖與事件綁定混在一起到底好不好?我們不是要講究視圖與邏輯分離嗎勇蝙?如此一來沫勿,把事件的綁定又變回了內(nèi)聯(lián)的,豈不是歷史的倒退味混。我也一樣對此表示不解产雹,因?yàn)椴粚憃nclick已經(jīng)很多年。翁锡。蔓挖。但既然已經(jīng)存在了,我們不妨往合理的方向上想一想馆衔,或許ng的設(shè)計(jì)者壓根就不想讓模板成為單純的視圖層瘟判,本來就是想增強(qiáng)HTML,讓它有一點(diǎn)業(yè)務(wù)能力角溃。這么想的話似乎也能想通拷获,好吧,先欺騙一下自己吧~
    4.6.4特殊的ng-src和ng-href
    在說明這兩個(gè)指令的特殊之前减细,需要先了解一下ng的啟動及執(zhí)行過程匆瓜,如下圖:
  1. 瀏覽器加載靜態(tài)HTML文件并解析為DOM;
      2) 瀏覽器加載angular.js文件未蝌;
      3) angular監(jiān)聽DOMContentLoaded 事件陕壹,監(jiān)聽到時(shí)開始啟動;
      4) angular尋找ng-app指令树埠,確定作用范圍糠馆;
      5) 找到app中定義的Module使用$injector服務(wù)進(jìn)行依賴注入;
      6) 根據(jù)$injector服務(wù)創(chuàng)建$compile服務(wù)用于編譯怎憋;
      7) $compile服務(wù)編譯DOM中的指令又碌、過濾器等;
      8) 使用ng-init指令绊袋,將作用域中的變量進(jìn)行替換毕匀;
      9) 最后生成了我們在最終視圖。
      可以看到癌别,ng框架是在DOMcontent加載完畢后才開始發(fā)揮作用皂岔。假如我們模板中有一張圖片如下:
      <img src="http://m.cnblogs.com/142260/”{{imgUrl}}” />
      那么在頁面開始加載到ng編譯完成之前,頁面上會一直顯示一張錯(cuò)誤的圖片展姐,因?yàn)槁窂絳{imgUrl}}還未被替換躁垛。
      為了避免這種情況剖毯,我們使用ng-src指令,這樣在路徑被正確得到之前就不會顯示找不到圖片教馆。同理逊谋,<a>標(biāo)簽的href屬性也需要換成ng-href,這樣頁面上就不會先出現(xiàn)一個(gè)地址錯(cuò)誤的鏈接土铺。
    順著這個(gè)思路再多想一點(diǎn)胶滋,我們在模板中使用{{}}顯示數(shù)據(jù)時(shí),在ng編譯完成之前頁面上豈不是會顯示出大括號及里面的表達(dá)式悲敷?確實(shí)是這樣究恤。為了避免這個(gè),ng中有一個(gè)與{{}}等同的指令:ng-bind后德,同樣用于單向綁定部宿,在頁面剛加載的時(shí)候就不會顯示出對用戶無用的數(shù)據(jù)了。盡管這樣你可能不但沒舒心反而更糾結(jié)了探遵,{{}}那么好用易理解窟赏,還不能用了不成妓柜?好消息是我們依然可以使用箱季。因?yàn)槲揖帉懙氖菃雾撁鎽?yīng)用,頁面只會在加載index.html的時(shí)
    候出這個(gè)問題棍掐,只需在index.html中的模板中換成ng-bind就行藏雏。其他的模板是我們動態(tài)加載的,就可以放心使用{{}}了作煌。
    4.6.5 自定義指令示例
    下面我們來解析下指令的例子(例07)掘殴。
    1.首先,我們定義一個(gè)名為userInfo的指令:
    demoApp.directive('userInfo',function(){
    return {
    restrict : 'E',
    templateUrl : 'userInfoTemplate.html',
    replace : true,
    transclude : true,
    scope : {
    mytitle : '=etitle'
    },
    link : function(scope,element,attrs){
    scope.showText = false;
    scope.toggleText = function(){
    scope.showText = ! scope.showText;
    }
    }
    };
    })
    Restrict為'E':用作標(biāo)簽粟誓;replace為true:用模板替換當(dāng)前標(biāo)簽奏寨;transclude為true:將當(dāng)前元素的內(nèi)容轉(zhuǎn)移到模板中望浩;scope 為 {mytitle : '=etitle'}:定義一個(gè)名為mytitle的MODEL祸泪,其值指向當(dāng)前元素的etitle屬性溉瓶;templateUrl為'userInfoTemplate.html':模板內(nèi)容為ng-template定義ID為userInfoTemplate.html的內(nèi)容拷橘;link:指定所包含的行為拙寡。其具體的說明及其他參數(shù)拗踢,請參考:6.2指令詳解尔觉。
  1. userInfoTemplate.html模板為:
    <script type="text/ng-template" id="userInfoTemplate.html">
    <div class="mybox">
    <div class="mytitle" style="cursor: pointer;" ng-click="toggleText()">
    { {mytitle} }
    </div>
    <div ng-transclude ng-show="showText">
    </div>
    </div>
    </script>
    將當(dāng)前元素的內(nèi)容添加到有ng-transclude屬性的這個(gè)DIV下环戈,默認(rèn)是隱藏的设易。
    3.Controller信息:
    demoApp.controller("test7Controller", function($scope){
    $scope.title = '個(gè)人簡介';
    $scope.text = '大家好逗柴,我正在研究AngularJs,歡迎大家與我交流顿肺。';
    $scope.updateInfo = function (){
    $scope.title = '個(gè)人信息';
    $scope.text = '大家好戏溺,今天天氣真好渣蜗!';
    }
    });
    4.指令使用方式(View信息)為:
    <user-info etitle="title">{ {text} }</user-info>
    Etitle指向Controller中的$scope.title。注意命名方式:指令名為userInfo于购,對應(yīng)的標(biāo)簽為user-info袍睡。
    4.7服務(wù)(service)
    4.7.1服務(wù)介紹
      服務(wù)這個(gè)概念其實(shí)并不陌生,在其他語言中如Java便有這樣的概念肋僧,其作用就是對外提供某個(gè)特定的功能斑胜,如消息服務(wù),文件壓縮服務(wù)等嫌吠,是一個(gè)獨(dú)立的模塊止潘。ng的服務(wù)是這樣定義的:
    Angular services are singletons objects or functions that carry out specific tasks common to web apps.
    它是一個(gè)單例對象或函數(shù),對外提供特定的功能辫诅。
    首先是一個(gè)單例凭戴,即無論這個(gè)服務(wù)被注入到任何地方,對象始終只有一個(gè)實(shí)例炕矮。
    其次這與我們自己定義一個(gè)function然后在其他地方調(diào)用不同么夫,因?yàn)榉?wù)被定義在一個(gè)模塊中,所以其使用范圍是可以被我們管理的肤视。ng的避免全局變量污染意識非常強(qiáng)档痪。
      ng提供了很多內(nèi)置的服務(wù),可以到API中查看http://docs.angularjs.org/api/邢滑。知道了概念腐螟,我們來拉一個(gè)service出來溜溜,看看到底是個(gè)什么用法困后±种剑  
      我們在controller中直接聲明$location服務(wù),這依靠ng的依賴注入機(jī)制摇予。$location提供地址欄相關(guān)的服務(wù)汽绢,我們在此只是簡單的獲取當(dāng)前的地址。
      服務(wù)的使用是如此簡單侧戴,我們可以把服務(wù)注入到controller宁昭、指令或者是其他服務(wù)中。
    4.7.2自定義服務(wù)
      如同指令一樣救鲤,系統(tǒng)內(nèi)置的服務(wù)以$開頭久窟,我們也可以自己定義一個(gè)服務(wù)。定義服務(wù)的方式有如下幾種:
    l 使用系統(tǒng)內(nèi)置的$provide服務(wù)本缠;
    l 使用Module的factory方法斥扛;
    l 使用Module的service方法。
      下面通過一個(gè)小例子來分別試驗(yàn)一下。我們定義一個(gè)名為remoteData服務(wù)稀颁,它可以從遠(yuǎn)程獲取數(shù)據(jù)芬失,這也是我們在程序中經(jīng)常使用的功能。不過我這里沒有遠(yuǎn)程服務(wù)器匾灶,就寫死一點(diǎn)數(shù)據(jù)模擬一下棱烂。
    //使用$provide來定義
    var app = angular.module('MyApp', [], function($provide) {
    $provide.factory('remoteData', function() {
    var data = {name:'n',value:'v'};
    return data;
    });
    });
    //使用factory方法
    app.factory('remoteData',function(){
    var data = {name:'n',value:'v'};
    return data;
    });
    //使用service方法
    app.service('remoteData',function(){
    this.name = 'n';
    this.value = 'v';
    });
    Module的factory和$provide的factory方法是一模一樣的,從官網(wǎng)文檔看它們其實(shí)就是一回事阶女。至于Module內(nèi)部是如何調(diào)用的颊糜,我此處并不打算深究,我只要知道怎么用就好了秃踩。
    再看Module的service方法衬鱼,它沒有return任何東西,是因?yàn)閟ervice方法本身返回一個(gè)構(gòu)造器憔杨,系統(tǒng)會自動使用new關(guān)鍵字來創(chuàng)建出一個(gè)對象鸟赫。所以我們看到在構(gòu)造器函數(shù)內(nèi)可以使用this,這樣調(diào)用該服務(wù)的地方便可以直接通過remoteData.name來訪問數(shù)據(jù)了消别。
    4.7.3管理服務(wù)的依賴關(guān)系
      服務(wù)與服務(wù)中間可以有依賴關(guān)系抛蚤,例如我們這里定義一個(gè)名為validate的服務(wù),它的作用是驗(yàn)證數(shù)據(jù)是否合法寻狂,它需要依賴我們從遠(yuǎn)程獲取數(shù)據(jù)的服務(wù)remoteData岁经。代碼如下:
      在factory的參數(shù)中,我們可以直接傳入服務(wù)remoteData荆虱,ng的依賴注入機(jī)制便幫我們做好了其他工作蒿偎。不過一定要保證這個(gè)參數(shù)的名稱與服務(wù)名稱一致朽们,ng是根據(jù)名稱來識別的怀读。若參數(shù)的名次與服務(wù)名稱不一致,你就必須顯示的聲明一下骑脱,方式如下:
    app.factory('validate',['remoteData',function(remoteDataService){
    return function(){
    if(remoteDataService.name=='n'){
    alert('驗(yàn)證通過');
    }
    };
    }]);  
    我們在controller中注入服務(wù)也是同樣的道理菜枷,使用的名稱需要與服務(wù)名稱一致才可以正確注入。否則叁丧,你必須使用$inject來手動指定注入的服務(wù)啤誊。比如:
    function testC(scope,rd){
    scope.getData = function(){
    alert('name:'+rd.name+' value:'+rd.value);
    }
    }
    testC.$inject = ['$scope','remoteData'];

在controller中注入服務(wù),也可以在定義controller時(shí)使用數(shù)組作為第二個(gè)參數(shù)拥娄,在此處
把服務(wù)注入進(jìn)去蚊锹,這樣在函數(shù)體中使用不一致的服務(wù)名稱也是可以的,不過要確保注入的順序是一致的稚瘾,如:
app.controller('testC',['$scope','remoteData',function($scope,rd){
$scope.getData = function(){
alert('name:'+rd.name+' value:'+rd.value);
}
}]);
4.7.4 自定義服務(wù)示例
接下來讓我們看下例子(例08 自定義服務(wù))代碼牡昆,自定義userService服務(wù):
demoApp.factory('userService', ['$http', function($http) {
var doGetUser = function(userId, path) {
//return $http({
//method: 'JSONP',
//url: path
//});
/手動指定數(shù)據(jù)/
var data = {userId:"woshishui",userName:"我是誰",userInfo:"我是誰!我是誰摊欠!"};;
if(userId=='zhangsan'){
data = {userId:"zhangsan",userName:"張三",userInfo:"我是張三丢烘,我為自己"};
}else if(userId=='lisi'){
data = {userId:"lisi",userName:"李四",userInfo:"我是李四柱宦,我為卿狂!"};
}
return data;
}
return {
/userService對外暴露的函數(shù)播瞳,可有多個(gè)/
getUser: function(userId) {
return doGetUser(userId, '../xxx/xxx.action');
}
};
}]);
我們創(chuàng)建了一個(gè)只有一個(gè)方法的userService掸刊,getUser為這個(gè)服務(wù)從后臺獲取用戶信息的函數(shù),并且對外暴露赢乓。當(dāng)然忧侧,由于這是一個(gè)靜態(tài)的例子,無法訪問后臺牌芋,那么我們便制定其返回的數(shù)據(jù)苍柏。
然后我們把這個(gè)服務(wù)添加到我們的controller中。我們建立一個(gè)controller并加載(或者注入)userService作為運(yùn)行時(shí)依賴姜贡,我們把service的名字作為參數(shù)傳遞給controller 函數(shù):
demoApp.controller("test8Controller", function($scope,userService){
/文章信息/
$scope.articles = [{
title : "愛飛像風(fēng)",
userId : "zhangsan",
userName : "張三"
},{
title : "無法停止的雨",
userId : "lisi",
userName : "李四"
}];
$scope.showUserInfo = false;//顯示作者詳細(xì)信息開關(guān)
$scope.currentUser = {}; //當(dāng)前選中的作者
$scope.getUserInfo = function(userId){
$scope.currentUser = userService.getUser(userId);
//調(diào)用 userService的getUser函數(shù)
$scope.showUserInfo = true;
setTimeout(function(){//定時(shí)器:隱藏作者詳細(xì)信息
$scope.showUserInfo = false;
},3000);
}
});
我們的userService注入到我們的test8Controller后试吁,我們就可以像使用其他服務(wù)(我們前面提到的$http服務(wù))一樣的使用userService了。
相關(guān)的HTML代碼如下:
/* View HTML/
<tr ng-repeat="article_ in articles">
<td>
{{article_.title}}
</td>
<td>
<a href="javascript:void(0);" target="_blank" rel="nofollow">
</td>
</tr>
......
<div ng-show="showUserInfo">
用戶ID:{{currentUser.userId}}

用戶名:{{currentUser.userName}}

用戶簡介:{{currentUser.userInfo}}

</div>
4.8依賴注入DI
通過依賴注入楼咳,ng想要推崇一種聲明式的開發(fā)方式熄捍,即當(dāng)我們需要使用某一模塊或服務(wù)時(shí),不需要關(guān)心此模塊內(nèi)部如何實(shí)現(xiàn)母怜,只需聲明一下就可以使用了余耽。在多處使用只需進(jìn)行多次聲明,大大提高可復(fù)用性苹熏。
  比如我們的controller碟贾,在定義的時(shí)候用到一個(gè)$scope參數(shù)。
app.controller('testC',function($scope){});  
如果我們在此處還需操作其他的東西轨域,比如與瀏覽器地址欄進(jìn)行交互袱耽。我們只需再多添
一個(gè)參數(shù)$location進(jìn)去:
app.controller('testC',function($scope,$location){});  
這樣便可以通過$location來與地址欄進(jìn)行交互了,我們僅僅是聲明了一下干发,所需的其他代碼朱巨,框架已經(jīng)幫我們注入了。我們很明顯的感覺到了這個(gè)函數(shù)已經(jīng)不是常規(guī)意義上的javascript函數(shù)了枉长,在常規(guī)的函數(shù)中冀续,把形參換一個(gè)名字照樣可以運(yùn)行,但在此處若是把$scope換成別的名字必峰,程序便不能運(yùn)行了洪唐。因?yàn)檫@是已經(jīng)定義好的服務(wù)名稱。
這便是依賴注入機(jī)制吼蚁。順理成章的推斷凭需,我們可以自己定義模塊和服務(wù),然后在需要的地方進(jìn)行聲明,由框架來替我們注入功炮。
來看下我們?nèi)绾味x一個(gè)服務(wù):
app.factory('tpls',function(){
return ['tpl1','tpl2','tpl3','tpl4'];
});  
看上去相當(dāng)簡單溅潜,是因?yàn)槲以谶@里僅僅是直接返回一個(gè)數(shù)組。在實(shí)際應(yīng)用中薪伏,這里應(yīng)該是需要向服務(wù)器發(fā)起一個(gè)請求滚澜,來獲取到這些模板們。服務(wù)的定義方式有好幾種嫁怀,包括使用provider方法设捐、使用factory方法,使用service方法塘淑。它們之間的區(qū)別暫且不關(guān)心萝招。我們現(xiàn)在只要能創(chuàng)建一個(gè)服務(wù)出來就可以了。我使用了factory方法存捺。一個(gè)需要注意的地方是槐沼,框架提供的服務(wù)名字都是由$開頭的,所以我們自己定義的最好不要用$開頭捌治,防止發(fā)生命名沖突岗钩。
定義好一個(gè)服務(wù)后,我們就可以在控制器中聲明使用了肖油,如下:
app.controller('testC',function($scope,tpls){
$scope.question = questionModel;
$scope.nowTime = new Date().valueOf();
$scope.templates = tpls; //賦值到$scope中
$scope.addOption = function(){
var o = {content:''};
$scope.question.options.push(o);
};
$scope.delOption = function(index){
$scope.question.options.splice(index,1);
};
});  
此時(shí)兼吓,若在模板中書寫如下代碼,我們便可以獲取到服務(wù)tpls所提供的數(shù)據(jù)了:
模板:
<a href="javascript:void(0);" target="_blank" rel="nofollow">
4.9路由(route)
在談路由機(jī)制前有必要先提一下現(xiàn)在比較流行的單頁面應(yīng)用森枪,就是所謂的single page APP视搏。為了實(shí)現(xiàn)無刷新的視圖切換,我們通常會用ajax請求從后臺取數(shù)據(jù)县袱,然后套上HTML模板渲染在頁面上浑娜,然而ajax的一個(gè)致命缺點(diǎn)就是導(dǎo)致瀏覽器后退按鈕失效,盡管我們可以在頁面上放一個(gè)大大的返回按鈕显拳,讓用戶點(diǎn)擊返回來導(dǎo)航棚愤,但總是無法避免用戶習(xí)慣性的點(diǎn)后退搓萧。解決此問題的一個(gè)方法是使用hash杂数,監(jiān)聽hashchange事件來進(jìn)行視圖切換,另一個(gè)方法是用HTML5的history API瘸洛,通過pushState()記錄操作歷史揍移,監(jiān)聽popstate事件來進(jìn)行視圖切換,也有人把這叫pjax技術(shù)反肋∧欠ィ基本流程如下:
如此一來,便形成了通過地址欄進(jìn)行導(dǎo)航的深度鏈接(deeplinking ),也就是我們所需要的路由機(jī)制罕邀。通過路由機(jī)制畅形,一個(gè)單頁應(yīng)用的各個(gè)視圖就可以很好的組織起來了。
4.9.1 ngRoute內(nèi)容
  ng的路由機(jī)制是靠ngRoute提供的诉探,通過hash和history兩種方式實(shí)現(xiàn)了路由日熬,可以檢測瀏覽器是否支持history來靈活調(diào)用相應(yīng)的方式。ng的路由(ngRoute)是一個(gè)單獨(dú)的模塊肾胯,包含以下內(nèi)容:
l 服務(wù)$routeProvider用來定義一個(gè)路由表竖席,即地址欄與視圖模板的映射
l 服務(wù)$routeParams保存了地址欄中的參數(shù),例如{id : 1, name : 'tom'}
l 服務(wù)$route完成路由匹配敬肚,并且提供路由相關(guān)的屬性訪問及事件毕荐,如訪問當(dāng)前路由對應(yīng)的controller
l 指令ngView用來在主視圖中指定加載子視圖的區(qū)域
 以上內(nèi)容再加上$location服務(wù),我們就可以實(shí)現(xiàn)一個(gè)單頁面應(yīng)用了艳馒。下面來看一下具體如何使用這些內(nèi)容憎亚。
4.9.2 ng的路由機(jī)制
  第一步:引入文件和依賴
  ngRoute模塊包含在一個(gè)單獨(dú)的文件中,所以第一步需要在頁面上引入這個(gè)文件弄慰,如下:
<script src="http://code.angularjs.org/1.2.8/angular.min.js" rel="nofollow"/>
<script src="http://code.angularjs.org/1.2.8/angular-route.min.js" rel="nofollow"/>  
光引入還不夠虽填,我們還需在模塊聲明中注入對ngRoute的依賴,如下:
var app = angular.module('MyApp', ['ngRoute']);  
完成了這些曹动,我們就可以在模板或是controller中使用上面的服務(wù)和指令了斋日。下面我們需要定義一個(gè)路由表。
  第二步:定義路由表
  $routeProvider提供了定義路由表的服務(wù)墓陈,它有兩個(gè)核心方法恶守,when(path,route)和otherwise(params),先看一下核心中的核心when(path,route)方法贡必。
  when(path,route)方法接收兩個(gè)參數(shù)兔港,path是一個(gè)string類型,表示該條路由規(guī)則所匹配的路徑仔拟,它將與地址欄的內(nèi)容($location.path)值進(jìn)行匹配衫樊。如果需要匹配參數(shù),可以在path中使用冒號加名稱的方式利花,如:path為/show/:name科侈,如果地址欄是/show/tom,那么參數(shù)name和所對應(yīng)的值tom便會被保存在$routeParams中炒事,像這樣:{name : tom}臀栈。我們也可以用
進(jìn)行模糊匹配,如:/show*/:name將匹配/showInfo/tom挠乳。
  route參數(shù)是一個(gè)object权薯,用來指定當(dāng)path匹配后所需的一系列配置項(xiàng)姑躲,包括以下內(nèi)容:
l controller //function或string類型。在當(dāng)前模板上執(zhí)行的controller函數(shù)盟蚣,生成新的scope黍析;
l controllerAs //string類型,為controller指定別名屎开;
l template //string或function類型橄仍,視圖z所用的模板,這部分內(nèi)容將被ngView引用牍戚;
l templateUrl //string或function類型侮繁,當(dāng)視圖模板為單獨(dú)的html文件或是使用了<script type="text/ng-template">定義模板時(shí)使用;
l resolve //指定當(dāng)前controller所依賴的其他模塊如孝;
l redirectTo //重定向的地址宪哩。
最簡單情況,我們定義一個(gè)html文件為模板第晰,并初始化一個(gè)指定的controller:
function emailRouteConfig($routeProvider){
$routeProvider.when('/show', {
controller: ShowController,
templateUrl: 'show.html'
}).
when('/put/:name',{
controller: PutController,
templateUrl: 'put.html'
});
};  
otherwise(params)方法對應(yīng)路徑匹配不到時(shí)的情況锁孟,這時(shí)候我們可以配置一個(gè)redirectTo參數(shù),讓它重定向到404頁面或者是首頁茁瘦。
  第三步:在主視圖模板中指定加載子視圖的位置
  我們的單頁面程序都是局部刷新的品抽,那這個(gè)“局部”是哪里呢,這就輪到ngView出馬了甜熔,只需在模板中簡單的使用此指令圆恤,在哪里用,哪里就是“局部”腔稀。例如:
<div ng-view></div>  或:<ng-view></ng-view>  
我們的子視圖將會在此處被引入進(jìn)來盆昙。完成這三步后,你的程序的路由就配置好了焊虏。
4.9.3 路由示例
下面我們將用一個(gè)例子(例09)來說明路由的使用方式及步驟:
1.為demoApp添加一個(gè)路由淡喜,代碼如下:
demoApp.config(['$routeProvider',function($routeProvider) {
$routeProvider.when('/list', {
templateUrl: 'route/list.html',
controller: 'routeListController'
}).when('/list/:id', {
templateUrl: 'route/detail.html',
controller: 'routeDetailController'
}).otherwise({
redirectTo: '/list'
});
}]);
/list 對應(yīng)為:route/list.html頁面,顯示用戶列表诵闭;/list/:id對應(yīng)于route/detail.html頁面炼团,顯示用戶詳細(xì)信息。
2.為list.html和detail.html分別聲明Controller:routeListController和routeDetailController疏尿。
demoApp.controller('routeListController',function($scope) {
$scope.users = [{userId:"zhangsan",userName:"張三",userInfo:"我是張三瘟芝,我為自己帶鹽!"},
{userId:"lisi",userName:"李四",userInfo:"我是李四润歉,我為卿狂模狭!"},
{userId:"woshishui",userName:"我是誰",userInfo:"我是誰!我是誰踩衩!我是誰嚼鹉!"}];

});
demoApp.controller('routeDetailController',function($scope, $routeParams, userService) {
$scope.userDetail = userService.getUser($routeParams.id);
});
routeDetailController中如上面提到的一樣,注入了userService服務(wù)驱富,在這里直接拿來用锚赤。
3.創(chuàng)建list.html和detail.html頁面,代碼如下:



<h3>Route : List.html(用戶列表頁面)</h3>
<ul>
<li ng-repeat="user in users">
<a href="http://m.cnblogs.com/142260/3817063.html?full=1#/list/{{ user.userId }}" target="_blank" rel="nofollow">
</li>
</ul>

<h3>Route : detail.html(用戶詳細(xì)信息頁面)</h3>
<h3>用戶名:<span style="color: red;">{{userDetail.userName}}</span></h3>
<div>
<span>用戶ID:{{userDetail.userId}}</span><span>用戶名:{{userDetail.userName}}</span>
</div>
<div>
用戶簡介:<span>{{userDetail.userInfo}}</span>
</div>
<div>
<a target="_blank" rel="nofollow">返回</a>
</div>

  1. 路由局部刷新位置:
    <h1>AngularJS路由(Route) 示例</h1>
    <div ng-view></div>
    4.10 NG動畫效果
    4.10.1 NG動畫效果簡介
    NG動畫效果褐鸥,現(xiàn)在可以通過CSS3或者是JS來實(shí)現(xiàn)线脚,如果是通過JS來實(shí)現(xiàn)的話,需要其他JS庫(比如JQuery)來支持叫榕,實(shí)際上底層實(shí)現(xiàn)還是靠其他JS庫浑侥,只是NG將其封裝了,
    使其更易使用晰绎。
    NG動畫效果包含以下幾種:
    enter:元素添加到DOM中時(shí)執(zhí)行動畫寓落;
    leave:元素從DOM刪除時(shí)執(zhí)行動畫;
    move:移動元素時(shí)執(zhí)行動畫荞下;
    beforeAddClass:在給元素添加CLASS之前執(zhí)行動畫伶选;
    addClass:在給元素添加CLASS時(shí)執(zhí)行動畫;
    beforeRemoveClass:在給元素刪除CLASS之前執(zhí)行動畫尖昏;
    removeClass:在給元素刪除CLASS時(shí)執(zhí)行動畫仰税。
    其相關(guān)參數(shù)為:
    var ngModule = angular.module('YourApp', ['ngAnimate']);
    demoApp.animation('.my-crazy-animation', function() {
    return {
    enter: function(element, done) {
    //run the animation here and call done when the animation is complete
    return function(cancelled) {
    //this (optional) function will be called when the animation
    //completes or when the animation is cancelled (the cancelled
    //flag will be set to true if cancelled).
    };
    },
    leave: function(element, done) { },
    move: function(element, done) { },
    //animation that can be triggered before the class is added
    beforeAddClass: function(element, className, done) { },
    //animation that can be triggered after the class is added
    addClass: function(element, className, done) { },
    //animation that can be triggered before the class is removed
    beforeRemoveClass: function(element, className, done) { },
    //animation that can be triggered after the class is removed
    removeClass: function(element, className, done) { }
    };
    });
    4.10.2 動畫效果示例
    下面我們來看下DEMO中的例子(例10)。
    1.首先抽诉,我們在demoApp下定義一個(gè)動畫效果陨簇,匹配CLASS:” .border-animation”
    /定義動畫/
    demoApp.animation('.border-animation', function(){
    return{
    beforeAddClass : function (element, className, done) {
    $(element).stop().animate({
    'border-width':1
    },2000, function() {
    done();
    });
    },
    removeClass : function (element ,className ,done ) {
    $(element).stop().animate({
    'border-width':50
    },3000, function() {
    done();
    });
    }
    };
    });
    動畫效果的含義就是:在匹配CLASS為border-animation的元素添加一個(gè)CLASS之前使其邊框的寬度在2秒內(nèi)變?yōu)?PX;并在其移除一個(gè)CLASS時(shí)使其邊框的寬度在3秒內(nèi)變?yōu)?0PX迹淌。
  2. 視圖中的代碼如下(主要塞帐,其他相關(guān)樣式請查看例子代碼):
    <div class="border-animation" ng-show="testShow"></div>
    <a href="javascript:void(0);" target="_blank" rel="nofollow">
    ng-show為false時(shí)會為其加上“ng-hide“的CLASS; ng-show為true時(shí)會為其移除“ng-hide“的CLASS,從而觸發(fā)動畫效果巍沙。
    3.其他代碼:
    demoApp.controller("test10Controller", function($scope, $animate){
    $scope.testShow = true;
    });
    5 功能演示
    略(詳情請看AngularJS/demo WEB演示)
    6 AngularJS進(jìn)階
    6.1數(shù)據(jù)綁定原理研究
    Angular用戶都想知道數(shù)據(jù)綁定是怎么實(shí)現(xiàn)的葵姥。你可能會看到各種各樣的詞匯:$watch、$apply句携、$digest喉悴、dirty-checking...它們是什么?它們是如何工作的呢隙袁?這里我想回答這些問題蚪拦,其實(shí)它們在官方的文檔里都已經(jīng)回答了,但是我還是想把它們結(jié)合在一起來講蠢笋,但是我只是用一種簡單的方法來講解拨齐,如果要想了解技術(shù)細(xì)節(jié),查看源代碼昨寞。
    6.1.1 AngularJS擴(kuò)展事件循環(huán)
    我們的瀏覽器一直在等待事件瞻惋,比如用戶交互厦滤。假如你點(diǎn)擊一個(gè)按鈕或者在輸入框里輸入東西,事件的回調(diào)函數(shù)就會在javascript解釋器里執(zhí)行歼狼,然后你就可以做任何DOM操作掏导,等回調(diào)函數(shù)執(zhí)行完畢時(shí),瀏覽器就會相應(yīng)地對DOM做出變化羽峰。(記住趟咆,這是個(gè)重要的概念),為了解釋什么是context以及它如何工作梅屉,我們還需要解釋更多的概念值纱。
    6.1.2 $watch 隊(duì)列
    每次你綁定一些東西到你的DOM上時(shí)你就會往$watch隊(duì)列里插入一條$watch。想象一下$watch就是那個(gè)可以檢測它監(jiān)視的model里時(shí)候有變化的東西坯汤。例如你有如下的代碼:
    /View index.html /
    User: <input type="text" ng-model="user" />
    Password: <input type="password" ng-model="pass" />
    在這里我們有個(gè)$scope.user虐唠,他被綁定在了第一個(gè)輸入框上,還有個(gè)$scope.pass玫霎,它被綁定在了第二個(gè)輸入框上凿滤,然后我們在$watch list里面加入兩個(gè)$watch。
    再看下面的例子:
    /
    Controller controllers.js /
    app.controller('MainCtrl', function($scope) {
    $scope.foo = "Foo";
    $scope.world = "World";
    });
    /
    View index.html /
    Hello, {{ World }}
    這里庶近,即便我們在$scope上添加了兩個(gè)東西翁脆,但是只有一個(gè)綁定在了DOM上,因此在這里只生成了一個(gè)$watch鼻种。
    再看下面的例子:
    /
    Controller controllers.js /
    app.controller('MainCtrl', function($scope) {
    $scope.people = [...];
    });
    /
    View index.html /
    <ul>
    <li ng-repeat="person in people">
    {{person.name}} - {{person.age}}
    </li>
    </ul>
    這里又生成了多少個(gè)$watch呢反番?每個(gè)person有兩個(gè)(一個(gè)name,一個(gè)age)叉钥,然后ng-repeat又有一個(gè)罢缸,因此10個(gè)person一共是(2 * 10) +1,也就是說有21個(gè)$watch。
    因此投队,每一個(gè)綁定到了DOM上的數(shù)據(jù)都會生成一個(gè)$watch枫疆。
    那這寫$watch是什么時(shí)候生成的呢?
    當(dāng)我們的模版加載完畢時(shí)敷鸦,也就是在linking階段(Angular分為compile階段和linking階段)息楔,Angular解釋器會尋找每個(gè)directive,然后生成每個(gè)需要的$watch扒披。
    6.1.3 $digest循環(huán)
    還記得我前面提到的擴(kuò)展的事件循環(huán)嗎值依?當(dāng)瀏覽器接收到可以被angular context處理的事件時(shí),$digest循環(huán)就會觸發(fā)碟案。這個(gè)循環(huán)是由兩個(gè)更小的循環(huán)組合起來的愿险。一個(gè)處理evalAsync隊(duì)列,另一個(gè)處理$watch隊(duì)列价说。 這個(gè)是處理什么的呢辆亏?$digest將會遍歷我們的$watch风秤,然后詢問:
    ?嘿,$watch褒链,你的值是什么唁情?
    ?是9疑苔。
    ?好的甫匹,它改變過嗎?
    ?沒有惦费,先生兵迅。
    ?(這個(gè)變量沒變過,那下一個(gè))
    ?你呢薪贫,你的值是多少恍箭?
    ?報(bào)告,是Foo瞧省。
    ?剛才改變過沒扯夭?
    ?改變過,剛才是Bar鞍匾。
    ?(很好交洗,我們有DOM需要更新了)
    ?繼續(xù)詢問直到$watch隊(duì)列都檢查過。
    這就是所謂的dirty-checking橡淑。既然所有的$watch都檢查完了构拳,那就要問了:有沒有$watch更新過?如果有至少一個(gè)更新過梁棠,這個(gè)循環(huán)就會再次觸發(fā)置森,直到所有的$watch都沒有變化。這樣就能夠保證每個(gè)model都已經(jīng)不會再變化符糊。記住如果循環(huán)超過10次的話凫海,它將會拋出一個(gè)異常,防止無限循環(huán)男娄。當(dāng)$digest循環(huán)結(jié)束時(shí)行贪,DOM相應(yīng)地變化。
    例如:
    /
    Controller controllers.js /
    app.controller('MainCtrl', function() {
    $scope.name = "Foo";
    $scope.changeFoo = function() {
    $scope.name = "Bar";
    }
    });
    /
    View index.html /
    {{ name }}
    <button ng-click="changeFoo()">Change the name</button>
    這里我們有一個(gè)$watch因?yàn)閚g-click不生成$watch(函數(shù)是不會變的)沪伙。
    我們可以看出ng的處理流程:
    ?我們按下按鈕瓮顽;
    ?瀏覽器接收到一個(gè)事件,進(jìn)入angular context围橡;
    ?$digest循環(huán)開始執(zhí)行暖混,查詢每個(gè)$watch是否變化;
    ?由于監(jiān)視$scope.name的$watch報(bào)告了變化翁授,它會強(qiáng)制再執(zhí)行一次$digest循環(huán)拣播;
    ?新的$digest循環(huán)沒有檢測到變化晾咪;
    ?瀏覽器拿回控制權(quán),更新與$scope.name新值相應(yīng)部分的DOM贮配。
    這里很重要的是每一個(gè)進(jìn)入angular context的事件都會執(zhí)行一個(gè)$digest循環(huán)谍倦,也就是說每次我們輸入一個(gè)字母循環(huán)都會檢查整個(gè)頁面的所有$watch。
    6.1.4如何進(jìn)入angular context
    誰決定什么事件進(jìn)入angular context泪勒,而哪些又不進(jìn)入呢昼蛀?通過$apply!
    如果當(dāng)事件觸發(fā)時(shí)圆存,你調(diào)用$apply叼旋,它會進(jìn)入angular context,如果沒有調(diào)用就不會進(jìn)入÷僬蓿現(xiàn)在你可能會問:剛才的例子里我也沒有調(diào)用$apply啊夫植,為什么?Angular已經(jīng)做了油讯!因此你點(diǎn)擊帶有ng-click的元素時(shí)详民,時(shí)間就會被封裝到一個(gè)$apply調(diào)用。如果你有一個(gè)ng-model="foo"的輸入框陌兑,然后你敲一個(gè)f沈跨,事件就會這樣調(diào)用$apply("foo = 'f';")。
    Angular什么時(shí)候不會自動為我們$apply呢诀紊?
    這是Angular新手共同的痛處谒出。為什么我的jQuery不會更新我綁定的東西呢?因?yàn)閖Query沒有調(diào)用$apply邻奠,事件沒有進(jìn)入angular context笤喳,$digest循環(huán)永遠(yuǎn)沒有執(zhí)行。
    我們來看一個(gè)有趣的例子:
    假設(shè)我們有下面這個(gè)directive和controller碌宴。
    /
    Controller app.js /
    app.directive('clickable', function() {
    return {
    restrict: "E",
    scope: {
    foo: '=',
    bar: '='
    },
    template: '<ul style="<li>{{foo}}</li><li>{{bar}}</li></ul>',
    link: function(scope, element, attrs) {
    element.bind('click', function() {
    scope.foo++;
    scope.bar++;
    });
    }
    }
    });
    app.controller('MainCtrl', function($scope) {
    $scope.foo = 0;
    $scope.bar = 0;
    });
    它將foo和bar從controller里綁定到一個(gè)list里面杀狡,每次點(diǎn)擊這個(gè)元素的時(shí)候,foo和bar都會自增1贰镣。那我們點(diǎn)擊元素的時(shí)候會發(fā)生什么呢呜象?我們能看到更新嗎?答案是否定的碑隆。因?yàn)辄c(diǎn)擊事件是一個(gè)沒有封裝到$apply里面的常見的事件恭陡,這意味著我們會失去我們的計(jì)數(shù)嗎?不會上煤。
    真正的結(jié)果是:$scope確實(shí)改變了休玩,但是沒有強(qiáng)制$digest循環(huán),監(jiān)視foo 和bar的$watch沒有執(zhí)行。也就是說如果我們自己執(zhí)行一次$apply那么這些$watch就會看見這些變化拴疤,然后根據(jù)需要更新DOM永部。
    執(zhí)行$apply:
    element.bind('click', function() {
    scope.foo++;
    scope.bar++;
    scope.$apply();
    });
    $apply是我們的$scope(或者是direcvie里的link函數(shù)中的scope)的一個(gè)函數(shù),調(diào)用它會強(qiáng)制一次$digest循環(huán)(除非當(dāng)前正在執(zhí)行循環(huán)呐矾,這種情況下會拋出一個(gè)異常苔埋,這是我們不需要在那里執(zhí)行$apply的標(biāo)志)。
    更好的使用$apply的方法:
    element.bind('click', function() {
    scope.$apply(function() {
    scope.foo++;
    scope.bar++;
    });
    })
    有什么不一樣的蜒犯?差別就是在第一個(gè)版本中组橄,我們是在angular context的外面更新的數(shù)據(jù),如果有發(fā)生錯(cuò)誤愧薛,Angular永遠(yuǎn)不知道晨炕。很明顯在這個(gè)像個(gè)小玩具的例子里面不會出什么大錯(cuò)衫画,但是想象一下我們?nèi)绻袀€(gè)alert框顯示錯(cuò)誤給用戶毫炉,然后我們有個(gè)第三方的庫進(jìn)行一個(gè)網(wǎng)絡(luò)調(diào)用然后失敗了,如果我們不把它封裝進(jìn)$apply里面削罩,Angular永遠(yuǎn)不會知道失敗了瞄勾,alert框就永遠(yuǎn)不會彈出來了。
    因此弥激,如果你想使用一個(gè)jQuery插件进陡,并且要執(zhí)行$digest循環(huán)來更新你的DOM的話,要確保你調(diào)用了$apply微服。
    有時(shí)候我想多說一句的是有些人在不得不調(diào)用$apply時(shí)會“感覺不妙”趾疚,因?yàn)樗麄儠X得他們做錯(cuò)了什么。其實(shí)不是這樣的以蕴,Angular不是什么魔術(shù)師糙麦,他也不知道第三方庫想要更新綁定的數(shù)據(jù)。
    6.1.5使用$watch來監(jiān)視
    你已經(jīng)知道了我們設(shè)置的任何綁定都有一個(gè)它自己的$watch丛肮,當(dāng)需要時(shí)更新DOM赡磅,但是我們?nèi)绻远x自己的watches呢?簡單宝与,來看個(gè)例子:
    /
    Controller app.js /
    app.controller('MainCtrl', function($scope) {
    $scope.name = "Angular";
    $scope.updated = -1;
    $scope.$watch('name', function() {
    $scope.updated++;
    });
    });
    /
    View index.html
    /
    <body ng-controller="MainCtrl">
    <input ng-model="name" />
    Name updated: {{updated}} times.
    </body>
    這就是我們創(chuàng)造一個(gè)新的$watch的方法焚廊。第一個(gè)參數(shù)是一個(gè)字符串或者函數(shù),在這里是只是一個(gè)字符串习劫,就是我們要監(jiān)視的變量的名字咆瘟,在這里,$scope.name(注意我們只需要
    用name)诽里。第二個(gè)參數(shù)是當(dāng)$watch說我監(jiān)視的表達(dá)式發(fā)生變化后要執(zhí)行的袒餐。我們要知道的第一件事就是當(dāng)controller執(zhí)行到這個(gè)$watch時(shí),它會立即執(zhí)行一次,因此我們設(shè)置updated為-1匿乃。
    例子2:
    /Controller app.js /
    app.controller('MainCtrl', function($scope) {
    $scope.name = "Angular";
    $scope.updated = 0;
    $scope.$watch('name', function(newValue, oldValue) {
    if (newValue === oldValue) { return; } // AKA first run
    $scope.updated++;
    });
    });
    /
    View index.html
    /
    <body ng-controller="MainCtrl">
    <input ng-model="name" />
    Name updated: {{updated}} times.
    </body>
    watch的第二個(gè)參數(shù)接受兩個(gè)參數(shù)桩皿,新值和舊值。我們可以用他們來略過第一次的執(zhí)行幢炸。通常你不需要略過第一次執(zhí)行泄隔,但在這個(gè)例子里面你是需要的。
    例子3:
    /Controller app.js /
    app.controller('MainCtrl', function($scope) {
    $scope.user = { name: "Fox" };
    $scope.updated = 0;
    $scope.$watch('user', function(newValue, oldValue) {
    if (newValue === oldValue) { return; }
    $scope.updated++;
    });
    });
    /
    View index.html
    /
    <body ng-controller="MainCtrl">
    <input ng-model="user.name" />
    Name updated: {{updated}} times.
    </body>
    我們想要監(jiān)視$scope.user對象里的任何變化宛徊,和以前一樣這里只是用一個(gè)對象來代替前面的字符串佛嬉。
    呃?沒用闸天,為啥暖呕?因?yàn)?watch默認(rèn)是比較兩個(gè)對象所引用的是否相同,在例子1和2里面苞氮,每次更改$scope.name都會創(chuàng)建一個(gè)新的基本變量湾揽,因此$watch會執(zhí)行,因?yàn)閷@個(gè)變量的引用已經(jīng)改變了笼吟。在上面的例子里库物,我們在監(jiān)視$scope.user,當(dāng)我們改變$scope.user.name時(shí)贷帮,對$scope.user的引用是不會改變的戚揭,我們只是每次創(chuàng)建了一個(gè)新的$scope.user.name,但是$scope.user永遠(yuǎn)是一樣的撵枢。
    例子4:
    /*Controller app.js */
    app.controller('MainCtrl', function($scope) {
    $scope.user = { name: "Fox" };

$scope.updated = 0;

$scope.$watch('user', function(newValue, oldValue) {
if (newValue === oldValue) { return; }
$scope.updated++;
}, true );
});
/View index.html/
<body ng-controller="MainCtrl">
<input ng-model="user.name" />
Name updated: {{updated}} times.
</body>
現(xiàn)在有用了吧民晒!因?yàn)槲覀儗?watch加入了第三個(gè)參數(shù),它是一個(gè)bool類型的參數(shù)锄禽,表示的是我們比較的是對象的值而不是引用潜必。由于當(dāng)我們更新$scope.user.name時(shí)$scope.user也會改變,所以能夠正確觸發(fā)沟绪。
6.1.6 總結(jié)
我希望你們已經(jīng)學(xué)會了在Angular中數(shù)據(jù)綁定是如何工作的刮便。我猜想你的第一印象是dirty-checking很慢,好吧绽慈,其實(shí)是不對的恨旱。它像閃電般快。但是坝疼,如果你在一個(gè)模版里有2000-3000個(gè)watch搜贤,它會開始變慢。但是我覺得如果你達(dá)到這個(gè)數(shù)量級钝凶,就可以找個(gè)用戶體驗(yàn)專家咨詢一下了仪芒。
無論如何,隨著ECMAScript6的到來,在Angular未來的版本里我們將會有Object.observe那樣會極大改善$digest循環(huán)的速度掂名。
6.2自定義指令詳解
angular的指令機(jī)制据沈。angular通過指令的方式實(shí)現(xiàn)了HTML的擴(kuò)展,增強(qiáng)后的HTML不僅長相煥然一新饺蔑,同時(shí)也獲得了很多強(qiáng)大的技能锌介。更厲害的是,你還可以自定義指令猾警,這就意味著HTML標(biāo)簽的范圍可以擴(kuò)展到無窮大孔祸。angular賦予了你造物主的能力。既然是作為angular的精華之一发皿,相應(yīng)的指令相關(guān)的知識也很多的崔慧。
6.2.1指令的編譯過程
  在開始自定義指令之前,我們有必要了解一下指令在框架中的執(zhí)行流程:
1.瀏覽器得到 HTML 字符串內(nèi)容穴墅,解析得到 DOM 結(jié)構(gòu)惶室。
2.ng 引入,把 DOM 結(jié)構(gòu)扔給 $compile 函數(shù)處理:
① 找出 DOM 結(jié)構(gòu)中有變量占位符封救;
② 匹配找出 DOM 中包含的所有指令引用拇涤;
③ 把指令關(guān)聯(lián)到 DOM;
④ 關(guān)聯(lián)到 DOM 的多個(gè)指令按權(quán)重排列誉结;
⑤ 執(zhí)行指令中的 compile 函數(shù)(改變 DOM 結(jié)構(gòu),返回 link 函數(shù))券躁;
⑥ 得到的所有 link 函數(shù)組成一個(gè)列表作為 $compile 函數(shù)的返回惩坑。

  1. 執(zhí)行 link 函數(shù)(連接模板的 scope)。
    這里注意區(qū)別一下$compile和compile也拜,前者是ng內(nèi)部的編譯服務(wù)以舒,后者是指令中的編譯函數(shù),兩者發(fā)揮作用的范圍不同慢哈。compile和link函數(shù)息息相關(guān)又有所區(qū)別蔓钟,這個(gè)在后面會講。了解執(zhí)行流程對后面的理解會有幫助卵贱。
    在這里有些人可能會問滥沫,angular不就是一個(gè)js框架嗎,怎么還能跟編譯扯上呢键俱,又不是像C++那樣的高級語言兰绣。其實(shí)此編譯非彼編譯,ng編譯的工作是解析指令编振、綁定監(jiān)聽器缀辩、替換模板中的變量等。因?yàn)楣ぷ鞣绞胶芟窀呒壵Z言編輯中的遞歸、堆棧過程臀玄,所以起名為編譯瓢阴,不要疑惑。
    6.2.2指令的使用方式及命名方法
      指令的幾種使用方式如下:
    作為標(biāo)簽:<my-dir></my-dir>
    作為屬性:<span my-dir="exp"></span>
    作為注釋:
    作為類名:<span class="my-dir: exp;"></span>
      其實(shí)常用的就是作為標(biāo)簽和屬性健无,下面兩種用法目前還沒見過炫掐,感覺就是用來賣萌的,姑且留個(gè)印象睬涧。我們自定義的指令就是要支持這樣的用法募胃。
    關(guān)于自定義指令的命名,你可以隨便怎么起名字都行畦浓,官方是推薦用[命名空間-指令名稱]這樣的方式痹束,像ng-controller。不過你可千萬不要用ng-前綴了讶请,防止與系統(tǒng)自帶的指令重名祷嘶。另外一個(gè)需知道的地方,指令命名時(shí)用駝峰規(guī)則夺溢,使用時(shí)用-分割各單詞论巍。如:定義myDirective,使用時(shí)像這樣:<my-directive>风响。
    6.2.3自定義指令的配置參數(shù)
    下面是定義一個(gè)標(biāo)準(zhǔn)指令的示例嘉汰,可配置的參數(shù)包括以下部分:
    myModule.directive('namespaceDirectiveName', function factory(injectables) {
    var directiveDefinitionObject = {
    restrict: string,//指令的使用方式,包括標(biāo)簽状勤,屬性鞋怀,類,注釋
    priority: number,//指令執(zhí)行的優(yōu)先級
    template: string,//指令使用的模板持搜,用HTML字符串的形式表示
    templateUrl: string,//從指定的url地址加載模板
    replace: bool,//是否用模板替換當(dāng)前元素密似,若為false,則append在當(dāng)前元素上
    transclude: bool,//是否將當(dāng)前元素的內(nèi)容轉(zhuǎn)移到模板中
    scope: bool or object,//指定指令的作用域
    controller: function controllerConstructor($scope, $element, $attrs, $transclude){...},//定義與其他指令進(jìn)行交互的接口函數(shù)
    require: string,//指定需要依賴的其他指令
    link: function postLink(scope, iElement, iAttrs) {...},//以編程的方式操作DOM葫盼,包
    括添加監(jiān)聽器等
    compile: function compile(tElement, tAttrs, transclude){
    return: {
    pre: function preLink(scope, iElement, iAttrs, controller){...},
    post: function postLink(scope, iElement, iAttrs, controller){...}
    }
    }//編程的方式修改DOM模板的副本残腌,可以返回鏈接函數(shù)
    };
    return directiveDefinitionObject;
    });
    看上去好復(fù)雜的樣子,定義一個(gè)指令需要這么多步驟嘛贫导?當(dāng)然不是抛猫,你可以根據(jù)自己的需要來選擇使用哪些參數(shù)。事實(shí)上priority和compile用的比較少脱盲,template和templateUrl又是互斥的邑滨,兩者選其一即可。所以不必緊張钱反,接下來分別學(xué)習(xí)一下這些參數(shù):
    l 指令的表現(xiàn)配置參數(shù):restrict掖看、template匣距、templateUrl、replace哎壳、transclude毅待;
    l 指令的行為配置參數(shù):compile和link;
    l 指令劃分作用域配置參數(shù):scope归榕;
    l 指令間通信配置參數(shù):controller和require尸红。
    6.2.3指令的表現(xiàn)參數(shù)restrict等
    指令的表現(xiàn)配置參數(shù):restrict、template刹泄、templateUrl外里、replace、transclude特石。
    我將先從一個(gè)簡單的例子開始盅蝗。
    例子的代碼如下:
    var app = angular.module('MyApp', [], function(){console.log('here')});
    app.directive('sayHello',function(){
    return {
    restrict : 'E',
    template : '<div>hello</div>'
    };
    })
    然后在頁面中,我們就可以使用這個(gè)名為sayHello的指令了姆蘸,它的作用就是輸出一個(gè)hello單詞墩莫。像這樣使用:
    <say-hello></say-hello>
    這樣頁面就會顯示出hello了,看一下生成的代碼:
    <say-hello>
    <div>hello</div>
    </say-hello>
      稍稍解釋一下我們用到的兩個(gè)參數(shù)逞敷,restirct用來指定指令的使用類型狂秦,其取值及含義如下:
    取值
    含義
    使用示例
    E
    標(biāo)簽
    <my-menu title=Products></my-menu>
    A
    屬性
    <div my-menu=Products></div>
    C

    <div class="my-menu":Products></div>
    M
    注釋

    默認(rèn)值是A。也可以使用這些值的組合推捐,如EA裂问,EC等等。我們這里指定為E,那么它就可以像標(biāo)簽一樣使用了。如果指定為A承疲,我們使用起來應(yīng)該像這樣:
    <div say-hello></div>
    從生成的代碼中鸥滨,你也看到了template的作用,它就是描述你的指令長什么樣子符喝,這部分內(nèi)容將出現(xiàn)在頁面中闪彼,即該指令所在的模板中,既然是模板中协饲,template的內(nèi)容中也可以使用ng-modle等其他指令畏腕,就像在模板中使用一樣。
    在上面生成的代碼中茉稠,我們看到了<div>hello</div>外面還包著一層<say-hello>標(biāo)簽描馅,如果我們不想要這一層多余的東西了,replace就派上用場了而线,在配置中將replace賦值為true铭污,將得到如下結(jié)構(gòu):
    <div>hello</div>
      replace的作用正如其名恋日,將指令標(biāo)簽替換為了temple中定義的內(nèi)容。不寫的話默認(rèn)為false嘹狞。
    上面的template未免也太簡單了岂膳,如果你的模板HTML較復(fù)雜,如自定義一個(gè)ui組件指令磅网,難道要拼接老長的字符串谈截?當(dāng)然不需要,此時(shí)只需用templateUrl便可解決問題涧偷。你可以將指令的模板單獨(dú)命名為一個(gè)html文件簸喂,然后在指令定義中使用templateUrl指定好文件的路徑即可,如:
    templateUrl : ‘helloTemplate.html’
    系統(tǒng)會自動發(fā)一個(gè)http請求來獲取到對應(yīng)的模板內(nèi)容燎潮。是不是很方便呢喻鳄,你不用糾結(jié)于拼接字符串的煩惱了。如果你是一個(gè)追求完美的有考慮性能的工程師跟啤,可能會發(fā)問:那這樣的話豈不是要犧牲一個(gè)http請求诽表?這也不用擔(dān)心,因?yàn)閚g的模板還可以用另外一種方式定義隅肥,那就是使用<script>標(biāo)簽竿奏。使用起來如下:
    <script type="text/ng-template" id="helloTemplate.html">
    <div>hello</div>
    </script>
    你可以把這段代碼寫在頁面頭部,這樣就不必去請求它了腥放。在實(shí)際項(xiàng)目中泛啸,你也可以將所有的模板內(nèi)容集中在一個(gè)文件中,只加載一次秃症,然后根據(jù)id來取用候址。
    接下來我們來看另一個(gè)比較有用的配置:transclude,定義是否將當(dāng)前元素的內(nèi)容轉(zhuǎn)移到模板中种柑「诼兀看解釋有點(diǎn)抽象,不過親手試試就很清楚了聚请,看下面的代碼(例06):
    app.directive('sayHello',function(){
    return {
    restrict : 'E',
    template : '<div>hello荠雕,<b ng-transclude></b>!</div>',
    replace : true,
    transclude : true
    };
    })
    指定了transclude為true驶赏,并且template修改了一下炸卑,加了一個(gè)<b>標(biāo)簽,并在上面使用了ng-transclude指令煤傍,用來告訴指令把內(nèi)容轉(zhuǎn)移到的位置盖文。那我們要轉(zhuǎn)移的內(nèi)容是什么呢?請看使用指令時(shí)的變化:
    <say-hello>美女</say-hello>
    內(nèi)容是什么你也看到了哈~在運(yùn)行的時(shí)候蚯姆,美女將會被轉(zhuǎn)移到<b>標(biāo)簽中五续,原來此配置的作用就是——乾坤大挪移洒敏!看效果:
    hello, 美女!
    這個(gè)還是很有用的返帕,因?yàn)槟愣x的指令不可能老是那么簡單桐玻,只有一個(gè)空標(biāo)簽。當(dāng)你需要對指令中的內(nèi)容進(jìn)行處理時(shí)荆萤,此參數(shù)便大有可用镊靴。
    6.2.4指令的行為參數(shù):compile和link
    6.2.3中簡單介紹了自定義一個(gè)指令的幾個(gè)簡單參數(shù),restrict链韭、template偏竟、templateUrl、replace敞峭、transclude踊谋,這幾個(gè)理解起來相對容易很多,因?yàn)樗鼈冎簧婕暗搅吮憩F(xiàn)旋讹,而沒有涉及行為殖蚕。我們繼續(xù)學(xué)習(xí)ng自定義指令的幾個(gè)重量級參數(shù):compile和link
    l 理解compile和link
      不知大家有沒有這樣的感覺,自己定義指令的時(shí)候跟寫jQuery插件有幾分相似之處沉迹,都是先預(yù)先定義好頁面結(jié)構(gòu)及監(jiān)聽函數(shù)睦疫,然后在某個(gè)元素上調(diào)用一下,該元素便擁有了特殊的功能鞭呕。區(qū)別在于蛤育,jQuery的側(cè)重點(diǎn)是DOM操作,而ng的指令中除了可以進(jìn)行DOM操作外葫松,更注重的是數(shù)據(jù)和模板的綁定瓦糕。jQuery插件在調(diào)用的時(shí)候才開始初始化,而ng指令在頁面加載進(jìn)來的時(shí)候就被編譯服務(wù)($compile)初始化好了腋么。
    在指令定義對象中咕娄,有compile和link兩個(gè)參數(shù),它們是做什么的呢珊擂?從字面意義上看谭胚,編譯、鏈接未玻,貌似太抽象了點(diǎn)。其實(shí)可大有內(nèi)涵胡控,為了在自定義指令的時(shí)候能正確使用它們扳剿,現(xiàn)在有必要了解一下ng是如何編譯指令的。
    l 指令的解析流程詳解
      我們知道ng框架會在頁面載入完畢的時(shí)候昼激,根據(jù)ng-app劃定的作用域來調(diào)用$compile服務(wù)進(jìn)行編譯庇绽,這個(gè)$compile就像一個(gè)大總管一樣锡搜,清點(diǎn)作用域內(nèi)的DOM元素,看看哪些元素上使用了指令(如<div ng-modle=”m”></div>)瞧掺,或者哪些元素本身就是個(gè)指令(如<mydierc></mydirec>)耕餐,或者使用了插值指令( {{}}也是一種指令,叫interpolation directive)辟狈,$compile大總管會把清點(diǎn)好的財(cái)產(chǎn)做一個(gè)清單肠缔,然后根據(jù)這些指令的優(yōu)先級(priority)排列一下,真是個(gè)細(xì)心的大總管哈~大總管還會根據(jù)指令中的配置參數(shù)(template哼转,place明未,transclude等)轉(zhuǎn)換DOM,讓指令“初具人形”壹蔓。
    然后就開始按順序執(zhí)行各指令的compile函數(shù)趟妥,注意此處的compile可不是大總管$compile,人家?guī)е?是土豪佣蓉,此處執(zhí)行的compile函數(shù)是我們指令中配置的披摄,compile函數(shù)中可以訪問到DOM節(jié)點(diǎn)并進(jìn)行操作,其主要職責(zé)就是進(jìn)行DOM轉(zhuǎn)換勇凭,每個(gè)compile函數(shù)執(zhí)行完后都會返回一個(gè)link函數(shù)疚膊,這些link函數(shù)會被大總管匯合一下組合成一個(gè)合體后的link函數(shù),為了好理解套像,我們可以把它想象成葫蘆小金剛酿联,就像是進(jìn)行了這樣的處理。
    //合體后的link函數(shù)
    function AB(){
    A(); //子link函數(shù)
    B(); //子link函數(shù)
    }  
    接下來進(jìn)入link階段夺巩,合體后的link函數(shù)被執(zhí)行贞让。所謂的鏈接,就是把view和scope鏈接起來柳譬。鏈接成啥樣呢喳张?就是我們熟悉的數(shù)據(jù)綁定,通過在DOM上注冊監(jiān)聽器來動態(tài)修改scope中的數(shù)據(jù)美澳,或者是使用$watchs監(jiān)聽 scope中的變量來修改DOM销部,從而建立雙向綁定。由此也可以斷定制跟,葫蘆小金剛可以訪問到scope和DOM節(jié)點(diǎn)舅桩。
    不要忘了我們在定義指令中還配置著一個(gè)link參數(shù)呢,這么多l(xiāng)ink千萬別搞混了雨膨。那這
    個(gè)link函數(shù)是干嘛的呢擂涛,我們不是有葫蘆小金剛了嘛?那我告訴你聊记,其實(shí)它是一個(gè)小三撒妈。此話怎講恢暖?compile函數(shù)執(zhí)行后返回link函數(shù),但若沒有配置compile函數(shù)呢狰右?葫蘆小金剛自然就不存在了杰捂。
    正房不在了,當(dāng)然就輪到小三出馬了棋蚌,大總管$compile就把這里的link函數(shù)拿來執(zhí)行嫁佳。這就意味著,配置的link函數(shù)也可以訪問到scope以及DOM節(jié)點(diǎn)附鸽。值得注意的是脱拼,compile函數(shù)通常是不會被配置的,因?yàn)槲覀兌x一個(gè)指令的時(shí)候坷备,大部分情況不會通過編程的方式進(jìn)行DOM操作熄浓,而更多的是進(jìn)行監(jiān)聽器的注冊、數(shù)據(jù)的綁定省撑。所以赌蔑,小三名正言順的被大總管寵愛。
    聽完了大總管竟秫、葫蘆小金剛和小三的故事娃惯,你是不是對指令的解析過程比較清晰了呢?不過細(xì)細(xì)推敲肥败,你可能還是會覺得情節(jié)生硬趾浅,有些細(xì)節(jié)似乎還是沒有透徹的明白,所以還需要再理解下面的知識點(diǎn):
    l compile和link的區(qū)別
      其實(shí)在我看完官方文檔后就一直有疑問馒稍,為什么監(jiān)聽器皿哨、數(shù)據(jù)綁定不能放在compile函數(shù)中,而偏偏要放在link函數(shù)中纽谒?為什么有了compile還需要link证膨?就跟你質(zhì)疑我編的故事一樣,為什么最后小三被寵愛了鼓黔?所以我們有必要探究一下央勒,compile和link之間到底有什么區(qū)別。好澳化,正房與小三的PK現(xiàn)在開始崔步。
    首先是性能。舉個(gè)例子:
    <ul>
    <li ng-repeat="a in array">
    <input ng-modle=”a.m” />
    </li>
    </ul>
    我們的觀察目標(biāo)是ng-repeat指令缎谷。假設(shè)一個(gè)前提是不存在link刷晋。大總管$compile在編譯這段代碼時(shí),會查找到ng-repeat,然后執(zhí)行它的compile函數(shù)眼虱,compile函數(shù)根據(jù)array的長度復(fù)制出n個(gè)<li>標(biāo)簽。而復(fù)制出的<li>節(jié)點(diǎn)中還有<input>節(jié)點(diǎn)并且使用了ng-modle指令席纽,所以compile還要掃描它并匹配指令捏悬,然后綁定監(jiān)聽器。每次循環(huán)都做如此多的工作润梯。而更加糟糕的一點(diǎn)是过牙,我們會在程序中向array中添加元素,此時(shí)頁面上會實(shí)時(shí)更新DOM纺铭,每次有新元素進(jìn)來寇钉,compile函數(shù)都把上面的步驟再走一遍,豈不是要累死了舶赔,這樣性能必然不行扫倡。
    現(xiàn)在扔掉那個(gè)假設(shè),在編譯的時(shí)候compile就只管生成DOM的事竟纳,碰到需要綁定監(jiān)聽器的地方先存著撵溃,有幾個(gè)存幾個(gè),最后把它們匯總成一個(gè)link函數(shù)锥累,然后一并執(zhí)行缘挑。這樣就輕松多了,compile只需要執(zhí)行一次桶略,性能自然提升语淘。
    另外一個(gè)區(qū)別是能力。
    盡管compile和link所做的事情差不多际歼,但它們的能力范圍還是不一樣的惶翻。比如正房能管你的存款,小三就不能蹬挺。小三能給你初戀的感覺维贺,正房卻不能。
    我們需要看一下compile函數(shù)和link函數(shù)的定義:
    function compile(tElement, tAttrs, transclude) { ... }
    function link(scope, iElement, iAttrs, controller) { ... }
    這些參數(shù)都是通過依賴注入而得到的巴帮,可以按需聲明使用溯泣。從名字也容易看出,兩個(gè)函數(shù)各自的職責(zé)是什么榕茧,compile可以拿到transclude垃沦,允許你自己編程管理乾坤大挪移的行為。而link中可以拿到scope和controller用押,可以與scope進(jìn)行數(shù)據(jù)綁定肢簿,與其他指令進(jìn)行通信。兩者雖然都可以拿到element,但是還是有區(qū)別的池充,看到各自的前綴了吧桩引?compile拿到的是編譯前的,是從template里拿過來的收夸,而link拿到的是編譯后的坑匠,已經(jīng)與作用域建立了
    關(guān)聯(lián),這也正是link中可以進(jìn)行數(shù)據(jù)綁定的原因卧惜。
      我暫時(shí)只能理解到這個(gè)程度了厘灼。實(shí)在不想理解這些知識的話,只要簡單記住一個(gè)原則就行了:如果指令只進(jìn)行DOM的修改咽瓷,不進(jìn)行數(shù)據(jù)綁定设凹,那么配置在compile函數(shù)中,如果指令要進(jìn)行數(shù)據(jù)綁定茅姜,那么配置在link函數(shù)中闪朱。
    6.2.5指令的劃分作用域參數(shù):scope
    我們在上面寫了一個(gè)簡單的<say-hello></say-hello>,能夠跟美女打招呼匈睁。但是看看人家ng內(nèi)置的指令监透,都是這么用的:ng-model=”m”,ng-repeat=”a in array”航唆,不單單是作為屬性胀蛮,還可以賦值給它,與作用域中的一個(gè)變量綁定好糯钙,內(nèi)容就可以動態(tài)變化了粪狼。假如我們的sayHello可以這樣用:<say-hello speak=”content”>美女</say-hello>,把要對美女說的話寫在一個(gè)變量content中任岸,然后只要在controller中修改content的值再榄,頁面就可以顯示對美女說的不同的話。這樣就靈活多了享潜,不至于見了美女只會說一句hello困鸥,然后就沒有然后。
    為了實(shí)現(xiàn)這樣的功能剑按,我們需要使用scope參數(shù)疾就,下面來介紹一下。
    使用scope為指令劃分作用域
      顧名思義艺蝴,scope肯定是跟作用域有關(guān)的一個(gè)參數(shù)猬腰,它的作用是描述指令與父作用域的關(guān)系,這個(gè)父作用域是指什么呢猜敢?想象一下我們使用指令的場景姑荷,頁面結(jié)構(gòu)應(yīng)該是這個(gè)樣子:
    <div ng-controller="testC">
    <say-hello speak="content">美女</say-hello>
    </div>  
    外層肯定會有一個(gè)controller盒延,而在controller的定義中大體是這個(gè)樣子:
    var app = angular.module('MyApp', [], function(){console.log('here')});
    app.controller('testC',function($scope){
    $scope.content = '今天天氣真好!';
    }); 
    所謂sayHello的父作用域就是這個(gè)名叫testC的控制器所管轄的范圍鼠冕,指令與父作用域的關(guān)系可以有如下取值:
    取值
    說明
    false
    默認(rèn)值添寺。使用父作用域作為自己的作用域
    true
    新建一個(gè)作用域,該作用域繼承父作用域
    javascript對象
    與父作用域隔離供鸠,并指定可以從父作用域訪問的變量
    乍一看取值為false和true好像沒什么區(qū)別畦贸,因?yàn)槿≈禐閠rue時(shí)會繼承父作用域,即父作用域中的任何變量都可以訪問到楞捂,效果跟直接使用父作用域差不多。但細(xì)細(xì)一想還是有區(qū)別的趋厉,有了自己的作用域后就可以在里面定義自己的東西寨闹,與跟父作用域混在一起是有本質(zhì)上的區(qū)別。好比是父親的錢你想花多少花多少君账,可你自己掙的錢父親能花多少就不好說了繁堡。你若想看這兩個(gè)作用域的區(qū)別,可以在link函數(shù)中打印出來看看乡数,還記得link函數(shù)中可以訪問到scope吧椭蹄。
    最有用的還是取值為第三種,一個(gè)對象净赴,可以用鍵值來顯式的指明要從父作用域中使用屬性的方式绳矩。當(dāng)scope值為一個(gè)對象時(shí),我們便建立了一個(gè)與父層隔離的作用域玖翅,不過也不是完全隔離翼馆,我們可以手工搭一座橋梁,并放行某些參數(shù)金度。我們要實(shí)現(xiàn)對美女說各種話就得靠這個(gè)应媚。使用起來像這樣:
    scope: {
    attributeName1: 'BINDING_STRATEGY',
    attributeName2: 'BINDING_STRATEGY',...
    }  
    鍵為屬性名稱,值為綁定策略猜极。等等中姜!啥叫綁定策略?最討厭冒新名詞卻不解釋的行為跟伏!別急丢胚,聽我慢慢道來。

先說屬性名稱吧酬姆,你是不是認(rèn)為這個(gè)attributeName1就是父作用域中的某個(gè)變量名稱嗜桌?錯(cuò)!其實(shí)這個(gè)屬性名稱是指令自己的模板中要使用的一個(gè)名稱辞色,并不對應(yīng)父作用域中的變量骨宠,稍后的例子中我們來說明浮定。再來看綁定策略,它的取值按照如下的規(guī)則:
符號
說明
舉例
@
傳遞一個(gè)字符串作為屬性的值
str : ‘@string’
=
使用父作用域中的一個(gè)屬性层亿,綁定數(shù)據(jù)到指令的屬性中
name : ‘=username’
&
使用父作用域中的一個(gè)函數(shù),可以在指令中調(diào)用
getName : ‘&getUserName’
  總之就是用符號前綴來說明如何為指令傳值桦卒。你肯定迫不及待要看例子了,我們結(jié)合例子看一下匿又,小二方灾,上栗子~
舉例說明
我想要實(shí)現(xiàn)上面想像的跟美女多說點(diǎn)話的功能,即我們給sayHello指令加一個(gè)屬性碌更,通過給屬性賦值來動態(tài)改變說話的內(nèi)容 主要代碼如下:
app.controller('testC',function($scope){
$scope.content = '今天天氣真好裕偿!';
});
app.directive('sayHello',function(){
return {
restrict : 'E',
template: '<div>hello,<b ng-transclude></b>,{{ cont }}</div>',
replace : true,
transclude : true,
scope : {

         cont : '=speak'
     }
};

});
然后在模板中,我們?nèi)缦率褂弥噶睿?br> <div ng-controller="testC">
<say-hello speak=" content ">美女</say-hello>
</div>
看看運(yùn)行效果:
美女今天天氣真好痛单!
  執(zhí)行的流程是這樣的:
 『偌① 指令被編譯的時(shí)候會掃描到template中的{ {cont} },發(fā)現(xiàn)是一個(gè)表達(dá)式旭绒;
 ∧衩睢② 查找scope中的規(guī)則:通過speak與父作用域綁定,方式是傳遞父作用域中的屬性挥吵;
 ≈馗浮③ speak與父作用域中的content屬性綁定,找到它的值“今天天氣真好忽匈!”房午;
  ④ 將content的值顯示在模板中脉幢。
這樣我們說話的內(nèi)容content就跟父作用域綁定到了一其歪沃,如果動態(tài)修改父作用域的content的值,頁面上的內(nèi)容就會跟著改變嫌松,正如你點(diǎn)擊“換句話”所看到的一樣沪曙。
  這個(gè)例子也太小兒科了吧!簡單雖簡單萎羔,但可以讓我們理解清楚液走,為了檢驗(yàn)?zāi)闶遣皇钦娴拿靼琢耍梢运伎家幌氯绾涡薷闹噶疃x贾陷,能讓sayHello以如下兩種方式使用:
<span say-hello speak="content">美女</span>
<span say-hello="content" >美女</span>
  答案我就不說了缘眶,簡單的很。下面有更重要的事情要做髓废,我們說好了要寫一個(gè)真正能用的東西來著巷懈。接下來就結(jié)合所學(xué)到的東西來寫一個(gè)折疊菜單,即點(diǎn)擊可展開慌洪,再點(diǎn)擊一次就收縮回去的菜單顶燕。
控制器及指令的代碼如下(例07):
app.controller('testC',function($scope){
$scope.title = '個(gè)人簡介';
$scope.text = '大家好凑保,我是一名前端工程師,我正在研究AngularJs涌攻,歡迎大家與我交流';
});
app.directive('expander',function(){
return {
restrict : 'E',
templateUrl : 'expanderTemp.html',
replace : true,
transclude : true,
scope : {
mytitle : '=etitle'
},
link : function(scope,element,attris){
scope.showText = false;
scope.toggleText = function(){
scope.showText = ! scope.showText;
}
}
};
});
HTML中的代碼如下:

<script type="text/ng-template" id="expanderTemp.html">
<div class="mybox">
<div class="mytitle" ng-click="toggleText()">
{{mytitle}}
</div>
<div ng-transclude ng-show="showText">
</div>
</div>
</script>
<div ng-controller="testC">
<expander etitle="title">{{text}}</expander>
</div>
  還是比較容易看懂的畅姊,我只做一點(diǎn)必要的解釋焰盗。首先我們定義模板的時(shí)候使用了ng的一種定義方式<script type=”text/ng-template”id="expanderTemp.html">,在指令中就可以用templateUrl根據(jù)這個(gè)id來找到模板便脊。指令中的{{mytitle}}表達(dá)式由scope參數(shù)指定從etitle傳遞壤追,etitle指向了父作用域中的title朝群。為了實(shí)現(xiàn)點(diǎn)擊標(biāo)題能夠展開收縮內(nèi)容饮醇,我們把這部分邏輯放在了link函數(shù)中硕蛹,link函數(shù)可以訪問到指令的作用域,我們定義showText屬性來表示內(nèi)容部分的顯隱鸵膏,定義toggleText函數(shù)來進(jìn)行控制租副,然后在模板中綁定好。 如果把showText和toggleText定義在controller中较性,作為$scope的屬性呢?顯然是不行的结胀,這就是隔離作用域的意義所在赞咙,父作用域中的東西除了title之外通通被屏蔽。
上面的例子中糟港,scope參數(shù)使用了=號來指定獲取屬性的類型為父作用域的屬性攀操,如果我們想在指令中使用父作用域中的函數(shù),使用&符號即可秸抚,是同樣的原理速和。
6.2.6指令間通信參數(shù):controller和require
  使用指令來定義一個(gè)ui組件是個(gè)不錯(cuò)的想法,首先使用起來方便剥汤,只需要一個(gè)標(biāo)簽或者屬性就可以了颠放,其次是可復(fù)用性高,通過controller可以動態(tài)控制ui組件的內(nèi)容吭敢,而且擁有雙向綁定的能力碰凶。當(dāng)我們想做的組件稍微復(fù)雜一點(diǎn),就不是一個(gè)指令可以搞定的了鹿驼,就需要指令與指令的協(xié)作才可以完成欲低,這就需要進(jìn)行指令間通信。
想一下我們進(jìn)行模塊化開發(fā)的時(shí)候的原理畜晰,一個(gè)模塊暴露(exports)對外的接口砾莱,另外一個(gè)模塊引用(require)它,便可以使用它所提供的服務(wù)了凄鼻。ng的指令間協(xié)作也是這個(gè)原理腊瑟,這也正是自定義指令時(shí)controller參數(shù)和require參數(shù)的作用聚假。
controller參數(shù)用于定義指令對外提供的接口,它的寫法如下:

controller: function controllerConstructor($scope, $element, $attrs, $transclude)  
它是一個(gè)構(gòu)造器函數(shù)扫步,將來可以構(gòu)造出一個(gè)實(shí)例傳給引用它的指令魔策。為什么叫controller(控制器)呢?其實(shí)就是告訴引用它的指令河胎,你可以控制我闯袒。至于可以控制那些東西呢,就需要在函數(shù)體中進(jìn)行定義了游岳。先看controller可以使用的參數(shù)政敢,作用域、節(jié)點(diǎn)胚迫、節(jié)點(diǎn)的屬性喷户、節(jié)點(diǎn)內(nèi)容的遷移,這些都可以通過依賴注入被傳進(jìn)來访锻,所以你可以根據(jù)需要只寫要用的參數(shù)褪尝。關(guān)于如何對外暴露接口,我們在下面的例子來說明期犬。
require參數(shù)便是用來指明需要依賴的其他指令河哑,它的值是一個(gè)字符串,就是所依賴的指令的名字龟虎,這樣框架就能按照你指定的名字來從對應(yīng)的指令上面尋找定義好的controller了璃谨。不過還稍稍有點(diǎn)特別的地方,為了讓框架尋找的時(shí)候更輕松些鲤妥,我們可以在名字前面加個(gè)小小的前綴:^佳吞,表示從父節(jié)點(diǎn)上尋找,使用起來像這樣:require : ‘^directiveName’棉安,如果不加底扳,$compile服務(wù)只會從節(jié)點(diǎn)本身尋找。另外還可以使用前綴:垂券?花盐,此前綴將告訴$compile服務(wù),如果所需的controller沒找到菇爪,不要拋出異常算芯。
所需要了解的知識點(diǎn)就這些,接下來是例子時(shí)間凳宙,依舊是從書上抄來的一個(gè)例子熙揍,我們要做的是一個(gè)手風(fēng)琴菜單,就是多個(gè)折疊菜單并列在一起氏涩,此例子用來展示指令間的通信再合適不過届囚。
首先我們需要定義外層的一個(gè)結(jié)構(gòu)有梆,起名為accordion,代碼如下:
app.directive('accordion',function(){
return {
restrict : 'E',
template : '<div ng-transclude></div>',
replace : true,
transclude : true,
controller :function(){
var expanders = [];
this.gotOpended = function(selectedExpander){
angular.forEach(expanders,function(e){
if(selectedExpander != e){
e.showText = false;
}
});
}
this.addExpander = function(e){
expanders.push(e);
}
}
}
});
需要解釋的只有controller中的代碼意系,我們定義了一個(gè)折疊菜單數(shù)組expanders泥耀,并且通過this關(guān)鍵字來對外暴露接口,提供兩個(gè)方法蛔添。gotOpended接受一個(gè)selectExpander參數(shù)用來修改數(shù)組中對應(yīng)expander的showText屬性值痰催,從而實(shí)現(xiàn)對各個(gè)子菜單的顯隱控制。addExpander方法對外提供向expanders數(shù)組增加元素的接口迎瞧,這樣在子菜單的指令中夸溶,便可以調(diào)用它把自身加入到accordion中。
看一下我們的expander需要做怎樣的修改呢:
app.directive('expander',function(){
return {
restrict : 'E',
templateUrl : 'expanderTemp.html',
replace : true,
transclude : true,
require : '^?accordion',
scope : {
title : '=etitle'
},

        link : function(scope,element,attris,accordionController){
            scope.showText = false;
            accordionController.addExpander(scope);
            scope.toggleText = function(){
                scope.showText = ! scope.showText;
                accordionController.gotOpended(scope);
            }
        }
    };
});

首先使用require參數(shù)引入所需的accordion指令凶硅,添加?^前綴表示從父節(jié)點(diǎn)查找并且失敗后不拋出異常缝裁。然后便可以在link函數(shù)中使用已經(jīng)注入好的accordionController了,調(diào)用addExpander方法將自己的作用域作為參數(shù)傳入足绅,以供accordionController訪問其屬性捷绑。然
后在toggleText方法中,除了要把自己的showText修改以外氢妈,還要調(diào)用accordionController的gotOpended方法通知父層指令把其他菜單給收縮起來胎食。
指令定義好后,我們就可以使用了允懂,使用起來如下:

<accordion>
<expander ng-repeat="expander in expanders" etitle="expander.title">
{{expander.text}}
</expander>
</accordion>  
外層使用了accordion指令,內(nèi)層使用expander指令衩匣,并且在expander上用ng-repeat循環(huán)輸出子菜單蕾总。請注意這里遍歷的數(shù)組expanders可不是accordion中定義的那個(gè)expanders,如果你這么認(rèn)為了琅捏,說明還是對作用域不夠了解生百。此expanders是ng-repeat的值,它是在外層controller中的柄延,所以蚀浆,在testC中,我們需要添加如下數(shù)據(jù):
$scope.expanders = [
{title: '個(gè)人簡介',
text: '大家好搜吧,我是一名前端工程師市俊,我正在研究AngularJs,歡迎大家與我交流'},
{title: '我的愛好',
text: 'LOL '},
{title: '性格',
text: ' 我的性格就是無性格'}
];
6.3 性能及調(diào)優(yōu)
6.3.1性能測試
AnglarJS作為一款優(yōu)秀的Web框架滤奈,可大大簡化前端開發(fā)的負(fù)擔(dān)摆昧。
AnglarJS很棒,但當(dāng)處理包含復(fù)雜數(shù)據(jù)結(jié)構(gòu)的大型列表時(shí)蜒程,其運(yùn)行速度就會非常慢绅你。
這是我們將核心管理頁面遷移到AngularJS過程中遇到的問題伺帘。這些頁面在顯示500行數(shù)據(jù)時(shí)本應(yīng)該工作順暢,但首個(gè)方法的渲染時(shí)間竟花費(fèi)了7秒忌锯,太可怕了伪嫁。后來,我們發(fā)現(xiàn)了在實(shí)現(xiàn)過程中存在兩個(gè)主要性能問題偶垮。一個(gè)與“ng-repeat ”指令有關(guān)张咳,另一個(gè)與過濾器有關(guān)。
AngularJS 中的ng-repeat在處理大型列表時(shí)针史,速度為什么會變慢晶伦?
AngularJS中的ng-repeat在處理2500個(gè)以上的雙向數(shù)據(jù)綁定時(shí)速度會變慢。這是由于AngularJS通過“dirty checking”函數(shù)來檢測變化啄枕。每次檢測都會花費(fèi)時(shí)間婚陪,所以包含復(fù)雜數(shù)據(jù)結(jié)構(gòu)的大型列表將降低你應(yīng)用的運(yùn)行速度。
提高性能的先決條件
時(shí)間記錄指令
為了測量一個(gè)列表渲染所花費(fèi)的時(shí)間频祝,我們寫了一個(gè)簡單的程序泌参,通過使用“ng-repeat”的屬性“$last”來記錄時(shí)間。時(shí)間存放在TimeTracker服務(wù)中常空,這樣時(shí)間記錄就與服務(wù)器端的數(shù)據(jù)加載分開了沽一。
// Post repeat directive for logging the rendering time
angular.module('siApp.services').directive('postRepeatDirective',
['$timeout', '$log', 'TimeTracker',
function($timeout, $log, TimeTracker) {
return function(scope, element, attrs) {
if (scope.$last){
$timeout(function(){
var timeFinishedLoadingList = TimeTracker.reviewListLoaded();
var ref = new Date(timeFinishedLoadingList);
var end = new Date();
$log.debug("## DOM rendering list took: " + (end - ref) + " ms");
});
}
};
}
]);
// Use in HTML:
<tr ng-repeat="item in items" post-repeat-directive>…</tr>
Chrome開發(fā)者工具的時(shí)間軸(Timeline)屬性
在Chrome開發(fā)者工具的時(shí)間軸標(biāo)簽中,你可以看見事件漓糙、每秒內(nèi)瀏覽器幀數(shù)和內(nèi)存分配铣缠。“memory”工具用來檢測內(nèi)存泄漏昆禽,及頁面所需的內(nèi)存蝗蛙。當(dāng)幀速率每秒低于30幀時(shí)就會出現(xiàn)頁面閃爍問題∽肀睿“frames”工具可幫助了解渲染性能捡硅,還可顯示出一個(gè)JavaScript任務(wù)所花費(fèi)的CPU時(shí)間。
通過限制列表的大小進(jìn)行基本的調(diào)優(yōu)
緩解該問題盗棵,最好的辦法是限制所顯示列表的大小壮韭。可通過分頁纹因、添加無限滾動條來實(shí)現(xiàn)喷屋。
分頁,我們可以使用AngularJS的“l(fā)imitTo”過濾器(AngularJS1.1.4版本以后)和“startFrom”過濾器瞭恰”泼桑可以通過限制顯示列表的大小來減少渲染時(shí)間。這是減少渲染時(shí)間最高效的方法。
6.3.2七大調(diào)優(yōu)法則
1.渲染沒有數(shù)據(jù)綁定的列表
這是最明顯的解決方案是牢,因?yàn)閿?shù)據(jù)綁定是性能問題最可能的根源僵井。如果你只想顯示一次列表,并不需要更新驳棱、改變數(shù)據(jù)批什,放棄數(shù)據(jù)綁定是絕佳的辦法。不過可惜的是社搅,你會失去對數(shù)據(jù)的控制權(quán)驻债,但除了該法,我們別無選擇形葬。
2.不要使用內(nèi)聯(lián)方法計(jì)算數(shù)據(jù)
為了在控制器中直接過濾列表合呐,不要使用可獲得過濾鏈接的方法◇弦裕“ng-repeat”會評估每個(gè)表達(dá)式淌实。在我們的案例中,“filteredItems()”返回過濾鏈接猖腕。如果評估過程很慢拆祈,它將迅速降低整個(gè)應(yīng)用的速度。

l <li ng-repeat="item in filteredItems()"> //這并不是一個(gè)好方法倘感,因?yàn)橐l繁地評估放坏。
l <li ng-repeat="item in items"> //這是要采用的方法
3.使用兩個(gè)列表(一個(gè)用來進(jìn)行視圖顯示,一個(gè)作為數(shù)據(jù)源)
將要顯示的列表與總的數(shù)據(jù)列表分開老玛,是非常有用的模型淤年。你可以對一些過濾進(jìn)行預(yù)處理,并將存于緩存中的鏈接應(yīng)用到視圖上蜡豹。下面案例展示了基本實(shí)現(xiàn)過程互亮。filteredLists變量保存著緩存中的鏈接,applyFilter方法來處理映射余素。
/* Controller /
// Basic list
var items = [{name:"John", active:true }, {name:"Adam"}, {name:"Chris"}, {name:"Heather"}];
// Init displayedList
$scope.displayedItems = items;
// Filter Cache
var filteredLists['active'] = $filter('filter)(items, {"active" : true});
// Apply the filter
$scope.applyFilter = function(type) {
if (filteredLists.hasOwnProperty(type){ // Check if filter is cached
$scope.displayedItems = filteredLists[type];
} else {
/
Non cached filtering /
}
}
// Reset filter
$scope.resetFilter = function() {
$scope.displayedItems = items;
}
/
View */
<button ng-click="applyFilter('active')">Select active</button>
<ul><li ng-repeat="item in displayedItems">{{item.name}}<li></ul>
4.在其他模板中使用ng-if來代替ng-show
如果你用指令、模板來渲染額外的信息炊昆,例如通過點(diǎn)擊來顯示列表項(xiàng)的詳細(xì)信息桨吊,一定要使用 ng-if(AngularJSv. 1.1.5以后)。ng-if可阻止渲染(與ng-show相比)凤巨。所以其它DOM和數(shù)據(jù)綁定可根據(jù)需要進(jìn)行評估视乐。
<li ng-repeat="item in items">

<p> {{ item.title }} </p>
<button ng-click="item.showDetails = !item.showDetails">Show details</buttons>
<div ng-if="item.showDetails">
{{item.details}}
</div>
</li>
5.不要使用ng-mouseenter、ng-mouseleave等指令
使用內(nèi)部指令敢茁,像ng-mouseenter佑淀,AngularJS會使你的頁面閃爍。瀏覽器的幀速率通常低于每秒30幀彰檬。使用jQuery創(chuàng)建動畫伸刃、鼠標(biāo)懸浮效果可以解決該問題谎砾。確保將鼠標(biāo)事件放入jQuery的.live()函數(shù)中。
6.關(guān)于過濾的小提示:通過ng-show隱藏多余的元素
對于長列表捧颅,使用過濾同樣會減低工作效率景图,因?yàn)槊總€(gè)過濾都會創(chuàng)建一個(gè)原始列表的子鏈接。在很多情況下碉哑,數(shù)據(jù)沒有變化挚币,過濾結(jié)果也會保持不變。所以對數(shù)據(jù)列表進(jìn)行預(yù)過濾扣典,并根據(jù)情況將它應(yīng)用到視圖中妆毕,會大大節(jié)約處理時(shí)間。
在ng-repeat指令中使用過濾器贮尖,每個(gè)過濾器會返回一個(gè)原始鏈接的子集笛粘。AngularJS 從DOM中移除多余元素(通過調(diào)用 $destroy),同時(shí)也會從$scope中移除他們远舅。當(dāng)過濾器的輸入發(fā)生改變時(shí)闰蛔,子集也會隨著變化,元素必須進(jìn)行重新鏈接图柏,或著再調(diào)用$destroy序六。
大部分情況下,這樣做很好蚤吹,但一旦用戶經(jīng)常過濾例诀,或者列表非常巨大,不斷的鏈接與
銷毀將影響性能裁着。為了加快過濾的速度繁涂,你可以使用ng-show和ng-hide指令。在控制器中二驰,進(jìn)行過濾扔罪,并為每項(xiàng)添加一個(gè)屬性。依靠該屬性來觸發(fā)ng-show桶雀。結(jié)果是矿酵,只為這些元素增加ng-hide類,來代替將它們移除子列表矗积、$scope和DOM全肮。
觸發(fā)ng-show的方法之一是使用表達(dá)式語法。ng-show的值由表達(dá)式語法來確定棘捣」枷伲可以看下面的例子:
<input ng-model="query"></input>
<li ng-repeat="item in items" ng-show="([item.name] | filter:query).length"> {{item.name}} </li>
<span style="font-size: 14px; line-height: 24px; font-family:; white-space: normal;"></span>
7.關(guān)于過濾的小提示:防抖動輸入
解決第6點(diǎn)提出的持續(xù)過濾問題的另一個(gè)方法是防抖動用戶輸入。例如,如果用戶輸入一個(gè)搜索關(guān)鍵詞评疗,只當(dāng)用戶停止輸入后测砂,過濾器才會被激活。使用該防抖動服務(wù)的一個(gè)很好的解決方案請見: http://jsfiddle.NET/Warspawn/6K7Kd/壤巷。將它應(yīng)用到你的視圖及控制器中邑彪,如下所示:
/* Controller /
// Watch the queryInput and debounce the filtering by 350 ms.
$scope.$watch('queryInput', function(newValue, oldValue) {
if (newValue === oldValue) { return; }
$debounce(applyQuery, 350);
});
var applyQuery = function() {
$scope.filter.query = $scope.query;
};
/
View */
<input ng-model="queryInput"/>
<li ng-repeat= item in items | filter:filter.query>{{ item.title }} </li>

7 總結(jié)
angular上手比較難,初學(xué)者(特別是習(xí)慣了使用JQuery的人)可能不太適應(yīng)其語法以及思想胧华。隨著對ng探索的一步步深入寄症,也確實(shí)感覺到了這一點(diǎn),尤其是框架內(nèi)部的某些執(zhí)行機(jī)制矩动。
7.1頁面效果
ng-show ng-hide 無動畫效果問題
7.2委派事件(代理事件)
7.2.1 NG循環(huán)及事件綁定
<ul>
<li ng-repeat="a in array">
<input ng-modle=”a.m” />
</li>
</ul>
Ng會根據(jù)array的長度復(fù)制出n個(gè)<li>標(biāo)簽有巧。而復(fù)制出的<li>節(jié)點(diǎn)中還有<input>節(jié)點(diǎn)并且使用了ng-modle指令,所以ng會對所有的<input>綁定監(jiān)聽器(事件)悲没。如果array很大篮迎,就會綁定太多的事件,性能出現(xiàn)問題示姿。
7.2.2 jQuery委派事件

從jQuery1.7開始甜橱,提供了.on()附加事件處理程序。
.on( events [, selector ] [, data ], handler(eventObject) )
參數(shù)Selector為一個(gè)選擇器字符串栈戳,用于過濾出被選中的元素中能觸發(fā)事件的后代元素岂傲。如果選擇器是 null 或者忽略了該選擇器,那么被選中的元素總是能觸發(fā)事件子檀。
如果省略selector或者是null镊掖,那么事件處理程序被稱為直接事件 或者 直接綁定事件 。每次選中的元素觸發(fā)事件時(shí)褂痰,就會執(zhí)行處理程序亩进,不管它直接綁定在元素上,還是從后代(內(nèi)部)元素冒泡到該元素的缩歪。
當(dāng)提供selector參數(shù)時(shí)归薛,事件處理程序是指為委派事件(代理事件)。事件不會在直接綁定的元素上觸發(fā)匪蝙,但當(dāng)selector參數(shù)選擇器匹配到后代(內(nèi)部元素)的時(shí)候主籍,事件處理函數(shù)才會被觸發(fā)。jQuery 會從 event target 開始向上層元素(例如骗污,由最內(nèi)層元素到最外層元素)開始冒泡,并且在傳播路徑上所有綁定了相同事件的元素若滿足匹配的選擇器沈条,那么這些元素上的事件也會被觸發(fā)需忿。
委托事件有兩個(gè)優(yōu)勢:他們能在后代元素添加到文檔后,可以處理這些事件;代理事件的另一個(gè)好處就是屋厘,當(dāng)需要監(jiān)視很多元素的時(shí)候涕烧,代理事件的開銷更小。
例如汗洒,在一個(gè)表格的 tbody 中含有 1,000 行议纯,下面這個(gè)例子會為這 1,000 元素綁定事
$("#dataTable tbody tr").on("click", function(event){ alert($(this).text());});
委派事件的方法只有一個(gè)元素的事件處理程序,tbody溢谤,并且事件只會向上冒泡一層(從被點(diǎn)擊的tr 到 tbody ):
$("#dataTable tbody").on("click", "tr", function(event){ alert($(this).text());});
許多委派的事件處理程序綁定到 document 樹的頂層附近瞻凤,可以降低性能。每次發(fā)生事件時(shí)世杀,jQuery 需要比較從 event target(目標(biāo)元素) 開始到文檔頂部的路徑中每一個(gè)元素上所有該類型的事件阀参。為了獲得更好的性能,在綁定代理事件時(shí)瞻坝,綁定的元素最好盡可能的靠近目標(biāo)元素蛛壳。避免在大型文檔中,過多的在 document 或 document.body 上添加代理事件所刀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末衙荐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子浮创,更是在濱河造成了極大的恐慌忧吟,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒸矛,死亡現(xiàn)場離奇詭異瀑罗,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)雏掠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門斩祭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人乡话,你說我怎么就攤上這事摧玫。” “怎么了绑青?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵诬像,是天一觀的道長。 經(jīng)常有香客問我闸婴,道長坏挠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任邪乍,我火速辦了婚禮降狠,結(jié)果婚禮上对竣,老公的妹妹穿的比我還像新娘。我一直安慰自己榜配,他們只是感情好否纬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛋褥,像睡著了一般临燃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上烙心,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天膜廊,我揣著相機(jī)與錄音,去河邊找鬼弃理。 笑死溃论,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的痘昌。 我是一名探鬼主播钥勋,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼辆苔!你這毒婦竟也來了算灸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤驻啤,失蹤者是張志新(化名)和其女友劉穎菲驴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骑冗,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赊瞬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贼涩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巧涧。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖遥倦,靈堂內(nèi)的尸體忽然破棺而出谤绳,到底是詐尸還是另有隱情,我是刑警寧澤袒哥,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布缩筛,位于F島的核電站,受9級特大地震影響堡称,放射性物質(zhì)發(fā)生泄漏瞎抛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一却紧、第九天 我趴在偏房一處隱蔽的房頂上張望桐臊。 院中可真熱鬧钞艇,春花似錦、人聲如沸豪硅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽懒浮。三九已至,卻和暖如春识藤,著一層夾襖步出監(jiān)牢的瞬間砚著,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工痴昧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留稽穆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓阳惹,卻偏偏與公主長得像搅荞,于是被迫代替她去往敵國和親栋荸。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容