AngularJs誕生於2009年,最初由Misko Hevery和Adam Abrons開發,後來成爲Google的項目。AngularJS 是一個爲動態WEB應用設計的結構框架。它能讓你使用HTML做爲模板語言,經過擴展HTML的語法,讓你能更清楚、簡潔地構建你的應用組件。它的創新點在於,利用 數據綁定 和 依賴注入,它使你不用再寫大量的代碼了。這些全都是經過瀏覽器端的Javascript實現,這也使得它可以完美地和任何服務器端技術結合。(達觀數據 陳高星)javascript
本文主要涵蓋:AngularJs MVC模型、$scope,controller和數據雙向綁定($apply(),$digest(),$watch)、module模塊、AngularJs路由、過濾器和自定義過濾器(filter)、服務和自定義服務(provider, factory,service)、指令和自定義指令(directive)、依賴注入(DI)、Angular繼承。html
MVC做爲web應用程序的一種優秀的設計模式,由model,veiw,controller三部分組成,它隔離了應用邏輯從用戶界面層和支持的關注點分離,因此常受歡迎。Model負責管理應用程序的數據。它響應來自視圖的請求,同時也響應指令從控制器進行自我更新。Veiw即視圖,它以一種特定的格式或者說樣式來顯示數據。Controller負責響應於用戶輸入並執行交互數據模型對象。控制器接收到輸入,它驗證輸入,而後執行修改數據模型的狀態的業務操做。一般認爲angular採用了MVC模型的設計模式(也有爭論認爲MVW或MVVM),後面涉及到的會較爲詳細解釋。前端
$scope 對象在 AngularJS 中充當數據模型的做用,也就是通常 MVC 框架中 Model 得角色.但又不徹底與一般意義上的數據模型同樣,由於 $scope 並不處理和操做數據。我理解$scope相似於種子或者橋樑的角色,使controller能夠獲取數據模型的信息。java
進一步系統的劃分它的做用和功能:
1.提供了觀察者能夠監聽數據模型的變化
2.能夠將數據模型的變化通知給整個 App
3.能夠進行嵌套,隔離業務功能和數據
4.給表達式提供上下文執行環境web
$scope相似於javascript的原型鏈 。搜索的時候,優先找本身的scope,若是沒有找到就沿着做用域鏈向上搜索,直至到達根做用域rootScope。$rootScope是由angularJS加載模塊的時候自動建立的,每一個模塊只會有1個rootScope。rootScope建立好會以服務的形式加入到 $injector中。也就是說經過 $injector.get("$ rootScope ");可以獲取到某個模塊的根做用域。更準確的來講,$rootScope是由angularJS的核心模塊ng建立的。
在AngularJS中,控制器Controller是一個Javascript函數(類型/類), 能經過表達式或者ng事件指令調用。(好比,ngClick),從而達處處理數據的目地。
設計模式
須要注意的一點是,一個控制器不該該作太多工做。它應該只包含單個視圖的業務邏輯,保持控制器職責單一的最多見作法是將那些不屬於控制器的工做抽離到服務中,而後經過依賴注入在控制器中使用這些服務。後面會討論依賴注入服務。數組
AngularJs最迷人的一點即是雙向數據綁定,AngularJS的工做原理是:HTML模板將會被瀏覽器解析到DOM中, DOM結構成爲AngularJS編譯器的輸入。AngularJS將會遍歷DOM模板, 來生成相應的NG指令,全部的指令都負責針對view(即HTML中的ng-model)來設置數據綁定。
在HTML中:
瀏覽器
在JS中:
服務器
可是須要注意,這種雙向綁定僅限於angular的上下文, 若是你在AngularJS上下文以外的任何地方修改了model,那麼你就須要經過手動調用$apply()來通知AngularJS。 例如:
app
這裏,咱們使用JavaScript的settimeout()來更新一個Scope Model,因此咱們把代碼wrapped到$scope.$apply()中(也能夠用$watch來監控model變化),它會自動觸發$rootScope.$digest(),從而讓watchers被觸發用以更新view。
若是全局的聲明Controller等等,這樣會污染全局命名空間。模塊化的方法還可讓代碼的複用更加便捷,單元測試也更加方便。例如:
AngularJS路由功能是一個純前端的解決方案,與咱們熟悉的後臺路由不太同樣。後臺路由,經過不一樣的URL會路由到不一樣的控制器上 (controller),再渲染(render)到頁面(HTML)。AngularJS的前端路由,需求提早對指定的(ng-app),定義路由規則 (routeProvider),而後經過不一樣的URL,告訴(ng-app)加載哪一個頁面(HTML),再渲染到(ng-app)視圖(ng- view)中。
目前單頁面應用愈來愈受歡迎,而Angular在構建單頁面應用上簡直是標配。這樣構建的單頁面應用的特色是單頁、無刷新式頁面變化,每一個頁面包含不一樣數據。
Angular路由由ngRoute模塊提供,須要引用angular-route.min.js。下面給出一個例子:
文檔結構:
Index.html 部分:
Js路由配置:
模版template:
ng-view 是一個用來包含當前路由(/home, /about, or /contact)的模板的angular指令, 它會得到基於特定路由的文件並將其諸如到主佈局中(index.html).
AngularJS過濾器用相似於管道的方式來格式化輸出給用戶的數據。除了格式化數據,過濾器還能修改DOM。這使得過濾器一般用來作些如「適時地給輸出加入CSS樣式」等工做。例如:
AngularJs容許自定義filter:在你的模塊中註冊一個新的過濾器(可注入的)工廠函數。這個工廠函數必須放回一個新的過濾器函數,這個過濾函數的第一個參數接受的是輸入。任何過濾器參數都會被當成附加的參數傳遞給過濾器。
能夠經過 | reverse 的方式使用reverse過濾器。
前面提到過Controller應該很薄,不要把全部業務邏輯和操做都放到Controller裏。應該說,大部分的業務邏輯和持久化數據應該放到Service裏。
Angular提供三種方式實現Service:Factory、Service、Provider。
1)用Factory就是建立一個對象,爲它添加屬性,而後把這個對象返回出來。你把 service 傳進 controller 以後,在 controller 裏這個對象裏的屬性就能夠經過 factory 使用了。
2)Service是用"new"關鍵字實例化的。所以,你應該給"this"添加屬性,而後 service 返回"this"。你把 service 傳進 controller 以後,在controller裏 "this" 上的屬性就能夠經過 service 來使用了。
3)Provider是惟一一種你能夠傳進 .config() 函數的 service。當你想要在 service 對象啓用以前,先進行模塊範圍的配置,那就應該用 provider。
指令使咱們用來擴展瀏覽器能力的技術之一。在DOM編譯期間,和HTML關聯着的指令會被檢測到,而且被執行。這使得指令能夠爲DOM指定行爲,或者改變它。
AngularJS有一套完整的、可擴展的、用來幫助web應用開發的指令集,它使得HTML能夠轉變成「特定領域語言(DSL)」。
上面在docsTransclusionExample模塊中建立了myDialog指令,那麼<myDialog></myDialog>標籤就會被解析成my-dialog.html模版的標籤。
Directive的幾個屬性:
Restrict:E(元素),A(屬性),C(類),M(註釋) 默認值是A
Scope:默認值false,表示繼承父做用域,true表示繼承父做用域並建立本身的做用域,{}表示建立一個全新的隔離做用域。當你想要建立一個可重用的組件時隔離做用域是一個很好的選擇,經過隔離做用域咱們確保指令是‘獨立’的,並能夠輕鬆地插入到任何HTML app中,而且這種作法防止了父做用域被污染。
Template:模版
TemplateUrl:加載指定Url模版
Link和compile負責渲染directive,compile在編譯前執行,負責把template(包括transclude所引用的)變成一個完整的DOM結構。link在編譯後執行,負責根據controller和scope,給compile獲得的DOM註冊事件、關聯數據等等。
Transclude:true,false或者element,true表示提取包含在指令那個元素裏面的內容,並可使用ng-transclude來將它放置在指令模板的特定位置。False表示不提取。Element表示提取整個元素。
Replace:true表示替換當前元素,false表示拼接。默認false。
關於什麼是依賴注入,在Stack Overflow上面有一個問題,如何向一個5歲的小孩解釋依賴注入,其中得分最高的一個答案是:
「When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn’t want you to have. You might even be looking for something we don’t even have or which has expired.
What you should be doing is stating a need, 「I need something to drink with lunch,」 and then we will make sure you have something when you sit down to eat.」
通常獲取依賴有三種方式,用new操做符建立依賴,經過全局變量查找,依賴須要時被導入。前兩種方式都不是很好,由於它們須要對依賴硬編碼,使得修改依賴的時候變得困難。特別是在測試的時候很差辦,由於對某個部分進行孤立的測試經常須要模擬它的依賴。第三種方式是最好的,由於它沒必要在組件中去主動需找和獲取依賴,而是由外界將依賴傳入。
Angular依賴注入的方式:
1)最簡單的處理依賴的方法,就是假設函數的參數名就是依賴的名字,給出一個注入器能夠經過檢查聲明來獲取函數名,從而知道須要的依賴的函數。
坦白的來說,用了這種方法就不能使用JavaScript minifiers/obfuscators(一些用來縮短的JS的類庫)了,由於它們會改變變量名。這使得這種方法只適合於pretotyping和作demo。
2)$inject標記:要容許壓縮類庫重命名函數參數,同時注入器又能正確處理依賴的話,函數須要使用$inject屬性。這個屬性是一個包含依賴的名稱的數組。注意$inject標記裏的值和函數聲明的參數是對應的。這種方式適合用於控制器的聲明,由於控制器有了明確的聲明標記。
3)行內標記:這種方法比較方便。下面$window爲注入依賴。
依賴注入再AngularJS中很廣泛。通常用在控制器和工場方法中。
控制器中的依賴注入:
工廠方法:工場方法負責建立AngularJS中的大部分對象。好比指令,服務,過濾器。工廠方法通常在模塊中使用。
AngularJS中沒有提供內建的用於繼承的特性,AngularJS組件中使用普通的JavaScript繼承模式。
1)做用域的原型繼承:原型繼承時對變量的賦值不會修改原型中的值,而是直接在當前scope中建立一個同名的屬性;但若是是變量是對象,則不會建立。即基本類型會從新建立變量,引用則不會。
H1,H2都顯示鍵入值。
2)控制器的繼承:子控制器的做用域將會原型繼承父控制器的做用域。所以當你須要重用來自父控制器中的功能時,你所要作的就是在父做用域中添加相應的方法。這樣一來,自控制器將會經過它的做用域的原型來獲取父做用域中的全部方法。
最後,筆者提醒,AngularJs 官網的API Reference提供了大量的指令、服務、過濾器等,深刻理解時你們不妨多多查詢。