angular學習(十五)——Provider
轉載請寫明來源地址:http://blog.csdn.net/lastsweetop/article/details/60966263
Provider簡單介紹
每一個web應用都是由多個對象協作完畢的。這些對象須要初始化並連接在一起為app服務。
在AngularJS中。app中的大多數對象通過injector服務初始化和連接在一起。
Injector創建兩種類型的對象,service對象和特別對象。
Service對象由開發人員自己定義api。
特別對象則遵照AngularJS框架特定的api,這些對象包括:controller, directive, filter or animation。
Injector須要知道怎麽樣創建這些對象,你能夠通過“配方”告訴它這些,總共同擁有五種“配方”。
最具體最全面的是Provider“配方”。其它四種(Value, Factory, Service and Constant)僅僅是在Provider“配方”之上包裝了一下而已。
“配方”
為了使injector知道怎麽樣創建和鏈接這些對象,你須要recipe的註冊表。
每一個recipe都有一個對象的id和怎樣創建對象的說明。
一個“配方”相應angularjs的一個module。module能夠包括一個或者多個“配方”,module還會包括依賴於其它模塊的信息。
當AngularJS使用給定的app module啟動app時,angularjs會創建一個新的injector實例,injector會依次把核心module的“配方”增加“配方”註冊表。而且創建app module和他的依賴。當須要為app創建對象時,就會去查找“配方”註冊表。
讓我們看下通過各種各樣“配方”類型創建和使用service的場景。
在你的代碼中可能多個地方須要訪問一個共享的字符串,我們先用Value“配方”來實現這樣的情境。
Value“配方”
我們來創建一個非常easy的service,該service提供一個用於遠程api鑒權的id字符串,能夠這麽寫:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="uft-8"/>
<title></title>
</head>
<script src="script/angular.min.js"></script>
<body ng-controller="DemoController as demo">
Client ID: {{demo.clientId}}
</body>
<script>
var myApp = angular.module(‘myApp‘, []);
myApp.value(‘clientId‘, ‘a12345654321x‘);
myApp.controller(‘DemoController‘, [‘clientId‘, function DemoController(clientId) {
this.clientId = clientId;
}]);
</script>
</html>
我們創建了一個名字叫myApp的module。而且指定該module的定義包括一個構建clientId服務的recipe,這個樣例中service僅僅是一個字符串。
這個樣例中我們使用value“配方”定義了一個值供DemoController須要一個clientId的service時調用。
factory“配方”
Value“配方”盡管簡單,可是缺少我們創建service時須要的非常多重要的功能,Factory“配方”就強大非常多,有以下功能:
- 能夠通過依賴使用其它service
- service初始化
- 延遲初始化
Factory“配方”能夠使用0個或者多個參數創建service,這些參數能夠是依賴的其它service。函數的返回值是一個通過“配方”創建的service實例。
在angularjs中全部的service都是單例的,這意味著injector為了創建對象僅僅使用每一個“配方”一次,然後injector把service的引用緩存起來。以便將來能夠調用。
Factory“配方”是個更加強大的類型,因此Value“配方”能夠做的事情Factory“配方”全然能夠做,我們把上個樣例使用Factory“配方”重寫一次:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="uft-8"/>
<title></title>
</head>
<script src="script/angular.min.js"></script>
<body ng-controller="DemoController as demo">
Client ID: {{demo.clientId}}
</body>
<script>
var myApp = angular.module(‘myApp‘, []);
//myApp.value(‘clientId‘, ‘a12345654321x‘);
myApp.factory("clientId",function(){
return "a12345654321x";
});
myApp.controller(‘DemoController‘, [‘clientId‘, function DemoController(clientId) {
this.clientId = clientId;
}]);
</script>
</html>
如果僅僅是返回一個token這麽簡單,那麽value“配方”則更合適,由於寫起來簡單也容易明確。
可是我們希望創建一個復雜點的service來計算token做遠程api的身份驗證,這個token叫做apiToken,通過clientId的值和瀏覽器本地存儲中的secret值計算得出。
以上代碼能夠看到怎樣依賴clientId服務通過Factory“配方”創建apiToken服務。
factory的函數名最好使用<serviceId>Factory
形式命名,比如apiTokenFactory
,當然函數名是能夠不要的,可是為了調試方便還是最好寫上。
同Value”配方“一樣,Factory”配方“能夠創建隨意類型的服務。不管是原始類型。對象,函數還是自己定義的類型。
service“配方”
javascript開發人員經常喜歡寫面向對象的自己定義類型,如今讓我們通過unicornLauncher服務把一個unicorn發射到太空,unicornLauncher是一個自己定義類型的實例。
function UnicornLauncher(apiToken) {
this.launchedCount = 0;
this.launch = function() {
// 使用apiToken訪問遠程api
...
this.launchedCount++;
}
}
如今我們準備發射獨角獸,註意到UnicornLauncher依賴於apiToken,我們通過Factory”配方“來滿足這個依賴:
myApp.factory(‘unicornLauncher‘, ["apiToken", function(apiToken) {
return new UnicornLauncher(apiToken);
}]);
然而這個樣例使用service“配方”更為合適
Service”配方“能夠像Value”配方“和Factory“配方”一樣生產service,可是它能夠通過new操作符調用對象的構造函數。
構造函數的參數能夠通過Service“配方中”的依賴項增加。
Service“配方”的設計模式叫做構造器註入(constructor injection)。
既然我已經有了UnicornLauncher多多構造器,我們能夠把Factory”配方”替換為Service“配方”。
myApp.service(‘unicornLauncher‘, ["apiToken", UnicornLauncher]);
Provider“配方”
Provider“配方”是核心“配方”,其它全部的“配方”僅僅是在它裏面加了點糖。
它是最多能力最具體的“配方”,可是對於大多數service來說。不是全部的能力都實用。
Provider“配方”定義了一個實現了$get
方法的自己定義類型。$get
方法是一個Factory函數,非常像Factory“配方”定義的Factory函數一樣。
其實。如果你定義了一個Factory“配方”。框架會創建一個空的Provider類型。當中的$get
方法就會指向你定義的Factory方法。
在app啟動時。我們須要一些配合時,這個時候才會須要Provider披露的API。一般是一些能夠重用的service。
默認情況下。發射器發射unicorn到太空是沒有不論什麽防護的。可是有些行星上大氣層太厚了,在unicorn開始它的太空旅行之前。我們須要為它包裹tinfoil。否則她沒通過大氣層時會摩擦燃燒。
配置代碼例如以下:
myApp.provider(‘unicornLauncher‘, function UnicornLauncherProvider() {
var useTinfoilShielding = false;
this.useTinfoilShielding = function(value) {
useTinfoilShielding = !!value;
};
this.$get = ["apiToken", function unicornLauncherFactory(apiToken) {
// 我們如果UnicornLauncher構造函數已更改,能夠接收useTinfoilShielding參數
return new UnicornLauncher(apiToken, useTinfoilShielding);
}];
});
在程序啟動時,config函數中調用unicornLauncherProvider的方式例如以下:
myApp.config(["unicornLauncherProvider", function(unicornLauncherProvider) {
unicornLauncherProvider.useTinfoilShielding(true);
}]);
unicornLauncherProvider是被註入到config方法中的,這裏的injector和常規的實例injector不同,它僅僅負責provider的初始化和連接。
在app啟動之時,在創建service之前。它會配置和實例化全部的provider,我們把這個叫做app生命周期的配置階段,在這個階段,service還不可使用。由於他們還未創建。
一旦配置階段完畢,那麽全部的provider變為不可用,創建service的進程開始啟動,這個階段被叫做app生命周期的執行階段。
Constant“配方”
我們剛剛了解了怎樣區分app生命周期的配置階段和執行階段。還有怎麽樣通過config函數配置app。由於配置函數在配置階段執行,這時service都是不能夠用的,它甚至無法訪問通過Value“配方”創建的簡單的值對象。
有一些簡單的值,比方url的前綴,沒有不論什麽依賴和配置,經常須要配置階段和執行階段都能夠方便的訪問。
Constant“配方”的作用就在這了。
如果我們的unicornLauncher服務須要使用它的所在行星的名字來標記。行星的名字能夠在配置階段提供。
行星的名字對於每一個app是特定的,執行階段會在各種各樣的controller中使用。我們能夠這樣這樣定義行星的名字作為一個常量:
myApp.constant(‘planetName‘, ‘Greasy Giant‘);
配置unicornLauncherProvider例如以下:
myApp.config([‘unicornLauncherProvider‘, ‘planetName‘, function(unicornLauncherProvider, planetName) {
unicornLauncherProvider.useTinfoilShielding(true);
unicornLauncherProvider.stampText(planetName);
}]);
Constant“配方”和Value“配方”一樣在配置階段是可用的,可用用於controller和模板:
myApp.controller(‘DemoController‘, ["clientId", "planetName", function DemoController(clientId, planetName) {
this.clientId = clientId;
this.planetName = planetName;
}]);
<html ng-app="myApp">
<body ng-controller="DemoController as demo">
Client ID: {{demo.clientId}}
<br>
Planet Name: {{demo.planetName}}
</body>
</html>
特殊用途的object
之前提到有些特殊用途的對象和service是不同的,這些對象作為插件來擴展angularjs框架。須要實現angularjs指定的接口。這些接口是Controller, Directive, Filter 和 Animation。
這些特殊對象除controller外,其余的在底層其實是由Factory“配方”創建的。
在以下的樣例中,我們演示怎樣通過Directive的api創建一個簡單的組件。這個組件依賴於我們之前定義的planetName常量,並顯示這個常量:“Planet Name: Greasy Giant”
由於Directive是通過Factory“配方”註冊的,我們採用Factory同樣的語法:
myApp.directive(‘myPlanet‘, [‘planetName‘, function myPlanetDirectiveFactory(planetName) {
// directive definition object
return {
restrict: ‘E‘,
scope: {},
link: function($scope, $element) { $element.text(‘Planet: ‘ + planetName); }
}
}]);
然後使用這個組件:
<html ng-app="myApp">
<body>
<my-planet></my-planet>
</body>
</html>
使用Factory“配方”也能夠定義filter和animation,可是controller有點特殊。你能創建controller作為一個自己定義類型,它的依賴能夠從它的構造函數中傳入,這個構造器又被module用來註冊controller。我們來看下我之前寫的DemoController:
myApp.controller(‘DemoController‘, [‘clientId‘, function DemoController(clientId) {
this.clientId = clientId;
}]);
每次app須要DemoController實例的時候。就好調用它的構造器進行初始化。
因此和service不一樣,controller不是單例的。構造器能夠註入其它service。
總結
讓我們來總結一下:
- injector使用“配方”創建兩種對象:service和特殊用途對象
- 有五種“配方”能夠創建對象:Value, Factory, Service, Provider, Constant.
- Factory和Service是使用最多的“配方”。他們之間唯一的差別是:Service“配方”更適合自己定義類型的對象,而Factory“配方”更適合原生對象和函數。
- Provider“配方”是最核心的“配方”,其它“配方”僅僅是在它上面加點糖。
- Provider“配方”是最復雜的“配方”,除非你須要全局配置的代碼,而這代碼你還想不斷復用。
- 全部的特殊對象除了controller都是使用Factory“配方”創建的
Features / Recipe type | Factory | Service | Value | Constant | Provider |
---|---|---|---|---|---|
是否能依賴與其它對象 | yes | yes | no | no | yes |
是否能友好的註入到其它對象 | no | yes | yes* | yes* | no |
創建的對象配置階段是否可用 | no | no | no | yes | yes** |
是否能創建函數 | yes | yes | yes | yes | yes |
是否能創建原生類型 | yes | no | yes | yes | yes |
* 須要使用new操作符付出預先初始化的代價
** service在配置階段不可用。可是provider對象在配置階段可用
angular學習(十五)——Provider