1. 程式人生 > >angular學習(十五)——Provider

angular學習(十五)——Provider

pri 驗證 module php http easy 核心 views detail

轉載請寫明來源地址: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