AngularJS 是公認的學習曲線相當陡的 Framework,因為它和一般的 MVC Framework 不太一樣,定義了像是 filter、directive 等功能,一開始要了解為何這樣設計就要花不少時間。而開始用 service 之後,對於 factory, service 和 provider 之間的差別又更是摸不著頭緒。其實整個 Framework 的運作和 DI (Dependency Injection) 有相當大的關係。

DI 是 AngularJS 的一大重點。它的目的就是讓應用程式的組件劃分的更加清楚,各部分各司其職。只要了解這個 Design pattern 的原理,應用程式會非常有條理,很容易維護和擴充。

先介紹 Module。Module 在 angular 中,可以比喻為一個袋子,可以用來裝著各式各樣的組件等著應用程式來取用。

模組 Module

angular.module('module1', []); // 定義一個module
angular.module('module1'); // 指向該module

// 定義各組件
angular.module('module1')
.value('a', 123)
.factory('b', function(){ return b123; })
.directive('a', ...)
.filter('a', ...);

value 和 factory 都屬於 service 類型,所以後面定義的會複寫掉前面的。
而 directive 和 filter 是特別的情況不受此限(後面會說明)。

接著在另外的module2中引用module1

angular.module('module2', ['module1'])
// 這裡如果使用 a 或 b 命名會擲出 Circular dependency 的錯誤
.factory('c', function(b){
    return 'c has dependency b: ' + b;
})

Bootstrap application

如下面的方式,在 html 的 div tag 中指定 module 的名稱之後,就會開始啟動應用程式。

<!-- html -->
<body ng-app="module2">
  <!-- your app -->
</body>

啟動 angular 應用程式分成兩個階段:config 及 run。
在 injector 產生 service 物件之前,會先執行所有的 config 設定,接著才進行 run 的部分。

Run

run 這個 method 會在所有的相依組件都設定完成後執行

angular.module('module2')
.run(function(a, b, c){
	console.log(a); // 123
    console.log(b); // b123
    console.log(c); // c has dependency b: b123
});

Provider

如前面所說,module 這個袋子有著各式各樣的組件。而負責調用這項工作的稱為 injector。

Injector 會產生兩種物件來提供給應用程式,一種通稱為"services",另一種稱作"special objects"。

有五種不同的service類型:value, constant, factory, service, provider。其實前面四個都是實作 provider 的方法來達成。

透過指出明確的形式來告訴 injector 該 service 的類型,讓 injector 來實作,因此官方用 Recipe 來形容這五種產生 service 的方式。

首先介紹 Value,非常單純,直接定義一個變數可以拿來 inject 到有需要的組件

angular.module('myModule')
.value('myValue', 'value is 1').
.run(function(){console.log(myValue)}) // value is 1

Factory 的彈性比較大,可以調用其他的dependency,也可以作初始化。

.factory('showMyValue', function(myValue){
	var greeting = 'Hello!';
    return greeting + '' + myValue;
})
.run(function(showMyValue){
	console.log(showMyValue); // Hello! value is 1
})

Service 有點 OO(object-oriented)的味道。他會回傳一個用 new syntax 產生出來的實體。而這個實體是 singleton 的,多次調用會回傳同一個實體,constructor 只會執行一次。

Service 的概念是讓開發者可以調用其他的 dependency,並創建一個實體在應用程式的其他地方自由調用。

.service('car', function(){
	var count = 0;
    count ++;
    console.log(count + ' car(s) made')
    this.run = function(){
    	console.log('car runs');
    };
})
.run(function(car){
	var car1 = car; // 1 car(s) made
    var car2 = car; // 沒有建構式的log訊息,service provider 直接回傳 singleton 實體
    car1 === car2; // true
})

因為是 singleton,所以也沒有傳參數到 constructor 的必要。所以無法傳入參數,只允許傳入其他的 dependency 在 service 內使用。

設定 config

沒有實體化的組件可以config之後再執行