AngularJS服务及注入--Provider

Provider简介

在AngularJS中,app中的大多数对象经过injector服务初始化和链接在一块儿。javascript

Injector建立两种类型的对象,service对象和特别对象。php

Service对象由开发者自定义api。html

特别对象则遵守AngularJS框架特定的api,这些对象包括:controller, directive, filter or animationjava

 

最详细最全面的是Provider,其余四种(Value, Factory, Service and Constant)只是在Provider“”之上包装了一下而已 angularjs

 

为了使injector知道怎么样建立和连接这些对象,你须要recipe的注册表。每个recipe都有一个对象的id和怎样建立对象的说明。express

当AngularJS使用给定的app module启动app时,angularjs会建立一个新的injector实例,injector会依次把核心module的“配方”加入“配方”注册表,而且建立app module和他的依赖。当须要为app建立对象时,就会去查找“配方”注册表。设计模式

 

Value 

代码中可能多个地方须要访问一个共享的字符串,咱们先用Value“配方”来实现这种情境。api

咱们来建立一个很是简单的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的引用缓存起来,以便未来能够调用。

示例:

<!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');
  • 1

配置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对象在配置阶段可用

相关文章
相关标签/搜索