一. 前言
AngularJS是一個JS的前端框架,跟後端的laravel, Django, Flask這些框架一樣也有MVC的架構,不一樣的地方是AngularJS的MVC架構是指針對前端而已。
AngularJS是Google於2010/10/20開發並維護的開源Javascript函式庫,目的是希望可以為後端減少了負擔,我這邊有整理來他的主要特點,與Javascript較不同的地方:
- 關注點分離(MVC)
2. 提供Directives的宣告方式
3. Two Way Data-Binding
4. Dependency Injection
二. 關注點分離(MVC)
先簡單介紹一下angularJS的code,可以下面有3個tag,分別是ng-app, ng-model與ng-bind,ng-app宣告使用angularJS的範圍,ng-model將元素值綁定在AngularJS中,ng-bind將變數值綁定在view上,只需在model的地方輸入值(例子是輸入eating),ng-bind與{{name}}中的值皆會改變,也就是說ng-model與view之間的關係是連動的,但如果你今天希望ng-model的值可以做些計算來改變的話這時就需要controller的幫助了,這就形成了所謂的MVC的關係。
過去比較常聽到的MVC,M是資料庫管理,V是頁面顯示,C是連結V與M的橋樑,其實AngularJS也差不多,只是他是關注於前端的部份而已,以下是我的理解:
Model: 欲輸出或計算的資料
View: 顯示介面資料(ex: ng-class, ng-show)
Controller: Model 跟 View 中間的橋樑,利用scope將View與Model做互動
下圖是來自T克邦的圖,這張圖畫的滿清楚,當使用者要求request一開始是由controller接收(與laravel, django不同),controller會處理model的資料,看這model裡的資料是否要往後端撈或計算,controller處理完後就會將model的資料塞往view中:
下面我們來看一個範例,剛剛是直接呼叫ng-app來讓整個區塊都形成angularJS的應用範圍,但有時可能需要一些複雜的功能或模組化,需要用controller來處理(像上圖所示),AngularJS提供一個module的方法,可以驅動ng-app並自己定義controller,以下是module的使用方式:
簡單來說就是多幫ng-app命名,用module來驅動這個ng-app,這個app之後可以做許多服務(如controller, service等等),但html只支援頁面中第一個的ng-app,也就是說如果你有2個以上的ng-app,html只會呼叫第一個,那如何自訂義呼較多個ng-app,因為可能每個ng-app要做的事都不一樣,這時需要使用bootstrap這個function,但我這邊先不提XD。
接續上面如何啟動controller,我們已經有app這個變數代表angularJS的應用範圍,自訂義controller只需要利用app.controller即可,並在頁面增加ng-controller這個屬性後面接controller的名字,就能在controller做運算囉~~
這邊各位可以看到有$scope這個變數,這變數就是存取在這個controller裡model的值,可以看到頁面定義了ng-model=”firstname”,那在controller只需要利用$scope.firstname就可以存取firstname這個存在AngularJS中的變數囉~~但$scope只能處理在自己controller中的變數,那如果我今天要宣告全域變數讓每個controller都能使用怎麼辦?
AngularJS提供了rootscope這個變數,在每個ng-app也就是angularJS的應用範圍都有一個rootScope變數,可以在每個controller中宣告要賦予rootScope的值,只要是在ng-app內皆可呼叫,如下圖code:
可以看到我再controller定義了$RootScope的lastname是”chen”,在頁面上不管是在controller裡或外面,只要是在ng-app裡皆可呼叫。
以上主要是AngularJS中MVC資料傳遞的方式拉~~
三. 提供Directives的宣告方式
從剛剛的一些範例可以看到有一些ng-app, ng-bind, ng-model…等等ng開頭的屬性,這些其實是AngularJS提供的屬性,那AngularJS真的滿貼心的,也提供我們自己定義這些屬性的方式,主要就是擴充HTML的屬性拉,那怎麼定義勒,如下圖所示:
angular.module(“myApp”, [])其實就是前面的app拉,再後面加上.directive即可,那命名方式像我是命名”etdirective”那在頁面上需向下面呼叫(駝峰式的命名方式):
那再前端顯示時會將<et-directive></et-directive>取代為剛剛我們定義的directive return的東西也就是會在html上顯示Hello Angular!這個字串,那其實他不一定要是html 元素的型式,directive提供一些參數:replace, template, templateUrl, restrict與scope,稍微說明一下用法:
- replace: 預設值 false,設為 true 時會用 template 或 templateUrl 的內容,來取代 directive 宣告的 HTML 元素,false則會將template 或 templateUrl 的內容往元素裡塞
- template、templateUrl: 輸入 HTML 模板
- scope: true/ false 或 物件,預設為false,與父scope共享一個model。設為true時,繼承父 scope 的 Object,並且產生一個新的 scope 用來放此Object,並且此scope不會干涉父scope。設為物件,會形成獨立的scope,不受父scope影響,並有3種參數,來限制參數形式
- restrict: 定義4種參數讓頁面可以以不同的方式呈現, 如下表格:
下面我用一些範例來說明:
這邊我定義了directive為etDirective,restrict設E表示他頁面會以html元素的方式呈現,replace沒寫預設為false,將template塞往<et-Directive></et-Directive>裡,scope設為false,即與父scope共享一個model,意思就是說scope的一個變數的值變了另一個地方一樣變數名稱的值也會變,下面是輸出的結果:
左邊是執行網頁後的結果,發現不管我在directive裡或是ng-model輸入2邊都會變化,這就是與父scope共享model的關係,這邊的父scope指的就是controller裡的scope。那我們來看看設為true的狀況(程式碼一樣,只是false改為true):
可以發現不同點是若父scope改變directive會跟著改變,但directive改變了,父scope並不會改變,原因是父scope還是可以存取directive,但directive自己創建了一個sub scope存取此變數並繼承了父scope,但並不會影響父scope的變化。
關於參數的定義目前我還沒做出範例XD,但推薦大家看這篇[1],directive寫的很清楚XD
四. Two Way Data-Binding
其實前面的範例應該多少都有這個Two Way Data-Binding的概念了XD,以下是one way與two way的差異:
- One Way Data-Binding: 當model改變時,view跟著改變
- Two Way Data-Binding: model改變時,view跟著改變; view改變時,model也會跟著改變
前面的範例可以看到其實AngularJS不管在view中或model中改變雙方皆會有變化,這就是Two Way Data-Binding的概念~~
五. Dependency Injection
這個中文翻譯是”依賴住入”拉,這其實是一個寫程式的概念,不算AngularJS提供的方法,只是AngularJS提供一些function讓這個特性更好的呈現,其實Dependency Injection的用意就是減低電腦代碼之間的耦合度,他是一種行為模式,讓各元件各司其職。
試想今天一個手機原本有A, B這2個功能,那今天評估後覺得B這個功能不需要,B這個功能可能比較適合放在筆電中,要另外多加C功能到手機中,若原本沒有將A B C這3個功能寫成模組化的型式,就要把手機重新Coding把A C寫在裏面,筆電也要多寫一個B,未來可能手機又要B這個功能又要寫上去,而且最嚴重的是沒有模組化的狀態這些變數可能會互相影響造成功能的耦合度過高,那若今天加入了Dependency Injection的概念,將這些功能模組化再需要的時候直接CALL這些模組不就更加方便,也不用擔心變數是否會互相影響,可以降低耦合度。
那AngularJS提供了Factory , Service 與 Provider實作的方法(這邊不說明這三種的差異),當我們定義好這些功能後再看今天我們這個產品需要什麼服務再利用AngularJS提供的$inject方法注入到這支程式即可,那具體怎麼做勒,先說明一下如何自訂一個service,只需利用app.service定義自己的服務名稱即可:
那如何啟用自己的服務,就是在呼叫controller時在function後面加入服務的名稱即可(這就是inject到這個controller中囉),如下圖。其實AngularJS內建已有30個服務了($http, $location等等),剩下就是看需求而定拉~~
但我不太建議上面這種寫法,controller, service還有自己的function都寫在一起了,我比較建議下面這種inject的寫法:
自訂controller後call自己寫的function,看這支function需要哪些服務用$inject的方式注入,在自己的function寫自己的邏輯,這樣寫比較分開,觀看起來也比較清楚。那向上面這樣寫,function的參數順序是按照inject的順序進來的,所以a代表scope,b代表location。
那上述主要是我整理出來AngularJS的特點,若哪裡有寫錯歡迎指證喔~~也可以給點敘述上建議~~
六. 參考資料
[1] https://dotblogs.com.tw/supershowwei/2016/09/05/001348
[2] https://www.w3schools.com/angular/
[3] https://zh.wikipedia.org/zh-tw/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC