博弈AngularJS讲义(16) - 提供者

  Angular框架提供了强大的依赖注入机制,这一切都是有注入器(injector)完成. 注入器会自动实例化服务组件和符合Angular API规则的特殊对象,例如控制器,指令,过滤器动画等。

  那注入器怎么知道如何去创建这些特殊的对象呢? Angular提供了5种方式让注入器创建对象,其中最基础的方式就是提供者(provider), 其余四种方式(Value, Factory, Service, Constant)都是基于Provider的语法糖。一个模块(module)就是一种配置注入器实例工厂的方式,我们也称为提供者(provider)。当Angular应用从一个模块开始启动时,Angular会为该模块创建注入器,注册在该模块以及所依赖的模块中定义的提供者,注入器会用提供者来创建app的对象。

  让我们通过如下几个例子来进一步了解上述五种方式的用法:

  例一: Value

   

var myApp = angular.module('myApp', []);
myApp.value('clientId', 'a12345654321x');

 

myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
  this.clientId = clientId;
}]);

   

<html ng-app="myApp">
  <body ng-controller="DemoController as demo">
    Client ID: {{demo.clientId}}
  </body>
</html>

 

    在myApp模块中通过Value的方式定义了clientId值, 在后面的代码中就可以通过注入的方式在控制器中注入clientId的值。

 

    例二:Factory

    Value的方式有其局限性,只适合定义简单类型, 工厂方式则有更多的灵活性:

    - 可以注入使用其他服务

    - 服务初始化

    - 惰性初始化

    工厂方法返回service实例,注意Angular的服务是单例的。 让我们看如下代码:

   

myApp.factory('apiToken', ['clientId', function apiTokenFactory(clientId) {
  var encrypt = function(data1, data2) {
    // NSA-proof encryption algorithm:
    return (data1 + ':' + data2).toUpperCase();
  };

  var secret = window.localStorage.getItem('myApp.secret');
  var apiToken = encrypt(clientId, secret);

  return apiToken;
}]);

   我们通过工厂方法返回了一个加密过的token, 在第二个参数中我们注入了例一中的clientId. 

 

  例三:Service

   在面向对象过程中我们通常使用自定义类型。该例中我们定义了UnicornLauncher构造方法,并通过service方法实例化unicornLauncher:

   

function UnicornLauncher(apiToken) {

  this.launchedCount = 0;
  this.launch = function() {
    // Make a request to the remote API and include the apiToken
    ...
    this.launchedCount++;
  }
}

myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);

 

  例四: 提供者

  提供者(Provider)为构造Angular应用的组件对象提供了最强大的支持。从语法层面讲Provider是$get方法的具体实现,是一个工厂方法类似例二的Factory. 对于构造service组件,使用上例中的Service方法就可以了,如果我们想给组件加上更多的启动配置项,增加组件的重用性,则考虑使用provider. 基于上面的例子我们给UnicornLauncher添加了“useTinfoilShielding”配置项:

  

myApp.provider('unicornLauncher', function UnicornLauncherProvider() {
  var useTinfoilShielding = false;

  this.useTinfoilShielding = function(value) {
    useTinfoilShielding = !!value;
  };

  this.$get = ["apiToken", function unicornLauncherFactory(apiToken) {

    // let's assume that the UnicornLauncher constructor was also changed to
    // accept and use the useTinfoilShielding argument
    return new UnicornLauncher(apiToken, useTinfoilShielding);
  }];
});

myApp.config(["unicornLauncherProvider", function(unicornLauncherProvider) {
  unicornLauncherProvider.useTinfoilShielding(true);
}]);

 

   通过provider api,定义了UnicornLauncherProvider,其中$get方法中定义了实例化UnicornLauncher的工厂方法,实例化方法参数中包含了useTinfoilShielding参数,该值由myApp.config配置。即Angular通过UnicornLauncherProvider实例化UnicornLauncher service. 注意在config方法中通常只能注入Provider,在应用启动时,Angular首先会配置并实例化所有的Provider实例,然后根据Provider来创建其他组件,在config阶段,service还未被实例化,所以不能在config方法中访问service组件。在config阶段结束后便进入run阶段,就不能再访问provider了,angular会根据provider的定义来初始化组件。

 

  例五:常量(Constant)

  上述例子中可见Angular应用启动分为两个阶段: config和run. 在config阶段services是不可用的,也不能访问通过Value初始的值类。 对于应用级别的常量,在config和run阶段皆可见,我们可以通过Constant来定义,参考如下示例代码:

  

myApp.constant('planetName', 'Greasy Giant');

myApp.config(['unicornLauncherProvider', 'planetName', function(unicornLauncherProvider, planetName) {
  unicornLauncherProvider.useTinfoilShielding(true);
  unicornLauncherProvider.stampText(planetName);
}]);

myApp.controller('DemoController', ["clientId", "planetName", function DemoController(clientId, planetName) {
  this.clientId = clientId;
  this.planetName = planetName;
}]);

   我们定义了planetName常量,在config和controller中都可以访问。

   

<html ng-app="myApp">
  <body ng-controller="DemoController as demo">
   Client ID: {{demo.clientId}}
   <br>
   Planet Name: {{demo.planetName}}
  </body>
</html>

 

   特殊组件

   前文中我们提到过,除了service组件,我们可以实现一些特殊组件,例如控制器,指令,过滤器和动画。这些组件可作为Angular框架的插件,实现了Angular框架特定的接口。这些接口也是基于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>

  

   上面例子中,我们通过directive来注册了myPlanet组件,和Factory一样,定义了myPlanetDirectiveFactory工厂方法,返回一个指令对象。同样我们也可通过工厂方法自定义指令和动画。

   相当于其他类型的组件,控制器没有使用工厂方法,而是构造器,因而控制器不是单例的。

   

myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
  this.clientId = clientId;
}]);

 

    让我们总结一些上面例子中的要点:

    - 注入器会通过提供者创建两种类型的组件: 服务组件(services)和特殊组件(controllers, directives, filters, animations).

    - Angular框架提供了5类提供者来创建对象: Value, Factory, Provider, Constant 和 Service, 其中Provider是最基础也是最强大的,另外四种都是基于Provider的封装。

    - Factory和Service是最常用的, 两者的区别是Service方法只返回某类型的对象,而Factory支持Javascript原始类型和函数。

    - Provider的用法相对复杂,除非需要为可重用组件提供全局的配置项,我们一般考虑使用其他的方式来构建组件。

    - 除了控制器使用构造器,其余特殊组件都使用工厂方式,控制器可以有多个实例,而其余特殊组件都是单例的。

    具体总结可参考如下表格(摘自官方文档):

   

Features / Recipe type Factory Service Value Constant Provider
can have dependencies yes yes no no yes
uses type friendly injection no yes yes* yes* no
object available in config phase no no no yes yes**
can create functions yes yes yes yes yes
can create primitives yes no yes yes yes

你可能感兴趣的:(js,AngularJS,api,Angular,Provider)