AngularJS服务及注入--Provider

Provider简介

在AngularJS中,app中的大多数对象通过injector服务初始化和连接在一起。

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

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

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

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

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

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

Value

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

我们来创建一个非常简单的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对象在配置阶段可用

时间: 2024-07-30 10:15:53

AngularJS服务及注入--Provider的相关文章

Angularjs 自定义服务 provide 里 provider 方法 以及 factory、 service 方法以及 provider 供应商的概念

Angularjs 自定义服务 provide 里 provider 方法 以及factory. service 方法以及 provider 供应商的概念 学习要点:1. Angularjs 中的 provider 方法2. Angularjs 中的 factory 方法3. Angularjs 中的 service 方法4. provider 供应商的概念5. 研究 ionic 代码中的 services 当你初试 Angular 时,很自然地就会往 controller 和 scope 里堆

AngularJS~集成的ajax和服务的注入

AngularJS很美,以至于迷倒了不少年青人和我这位大叔,它的美不仅仅是在写法上,而且在设计方法上都进乎于完美,用什么服务就注入什么服务,这样方法本来就很直观,程序员感觉直观了,程序在运行起来也按需要装载,这种按需要装载无论在性能上还是在表现力上都远远优于完全加载方式. Ajax加载数据 $http服务提供了一组ajax的方法,加载数据,Get,Post都有支持,而$http服务在angular里就是被动态装载的,在面向对象里叫做DI或者IOC angular.module('todoApp'

AngularJS服务总结

在之前介绍AngularJS的博客中,简单的介绍了AngularJS提供的常用服务,以及如何自定义服务,具体可参见<AngularJS 服务(Service)>.在这一篇博客中,我将为大家详细介绍如何自定义服务,以及各种方式之间的区别. value(name, object) value(name, object)方法允许我们直接将一个普通值或对象作为服务.我们通过一段代码来看看如何使用: <!DOCTYPE html> <html> <head> <

AngularJS服务

在使用AngularJS的路由管理和控制视图加载的时候,当新的视图加载进来,原有视图的控制器会被销毁,这是出于内存占用和性能的考虑.服务提供了一种能在应用的整个生命周期内保持和共享数据的方法,它能够在控制器之间进行通信,并且保持数据的一致性. 服务是一个单例对象,在每个应用中只会被实例化一次,并且是延迟加载的,服务提供了把与特定功能相关联的方法集中在一起的接口.就拿常用的$http服务为例,它提供了对浏览器的XMLHttpRequest对象的底层访问功能,我们可以通过$http的API与XMLH

在angular2服务中注入服务

http://kittencup.com/javascript/2015/11/11/%E5%9C%A8angular2%E6%9C%8D%E5%8A%A1%E4%B8%AD%E6%B3%A8%E5%85%A5%E6%9C%8D%E5%8A%A1.html 如果你关注我们的文章 Angular2中的依赖注入,你知道DI系统在Angular中是如果运作的,它利用在我们代码上通过注解添加metadata来获取所有关于依赖的信息来解决我们的依赖关系 Angular 2 应用基本上可以用任何语言编写.只

AngularJS DI(依赖注入)实现推测

AngularJS DI(依赖注入) http://www.cnblogs.com/whitewolf/archive/2012/09/11/2680659.html 回到angularjs:在框架中为我们提供了angular.injector(modules)DI注入注射器.但是在我们使用注入的时候常常是不需要关心具体的如何注入.我们只需要按照其规则书写我们的angularjs代码就会很容易的得到angularjs的DI特性 1:推断式注入:在angularjs中我们可以在我们需要注入的地方按

angularJS服务一

一 认识服务 1.服务这个概念其实并不陌生,在其他语言中如java便有这样的概念,其作用就是对外提供某个特定的功能,如消息服务,文件压缩服务等,是一个独立的模块.ng的服务是一个单例对象或函数,对外提供特定的功能. 2.特点:首先是一个单例,即无论这个服务被注入到任何地方,对象始终只有一个实例. 其次这与我们自己定义一个function然后在其他地方调用不同,因为服务被定义在一个模块中,所以其使用范围是可以被我们管理的.ng的避免全局变量污染意识非常强. 3.ng提供了很多内置的服务,如$loc

AngularJS的依赖注入方式

在定义controller,module,service,and directive时有两种方式, 方式一: var myModule = angular.module('myApp', []); myModule.controller('myCtrl', ['$scope', 'Project', function($scope, Project) { }]); 方式二: var myModule = angular.module('myApp', []); function myCtrl($

angularjs 服务

内置服务 1.$timeout 2.$watch(obj,fun): fun(newVal,oldVal,scope) //newVal是新改变的值,oldVal是旧值 3.$destroy():作用域的销毁,如果子作用域不再有用了.那么子作用域的创建者就会负责用$destroy()来将它销毁.这回停止$digest再调用子作用域,并且让作用域占用的内容能够被回收. 内置函数: 1.dateFilter(time,format)//参数time是时间,format是格式化 angularjs 服