首页 > 代码库 > 理解AngularJS中的Service类型

理解AngularJS中的Service类型

技术分享

 

Angular中有几种不同类型的services。每一种都有自己的独特用法。

需要记住的非常重要的一点是service总是一个单体,无论是哪种类型的service。

注释:单体是一种设计模式,它限制了每一个类仅能够实例化为一个对象。无论我们在什么地方注入我们的service,将永远使用同一个实例。

Constant

例子:

app.constant(‘fooConfig‘,{    config1: true,    config2: "Default config2"});   

 

Constant是一个非常有用的service,它经常被用来在指令中提供默认配置。因此如果你正在创建一个指令,并且你想要在给指令传递可选参数的同时进行一个默认配置,一个Constant就是一个好办法。

作为一个constant,我们放入其中的值将不会改变。Contant service 基本上回事一个基本类型的值或者是一个对象。

Value

例子:

app.value(‘fooConfig‘,{    config1: true,    config2: "Default config2 but it can change"});  

一个value service有点像是一个constant但是它是可以被改变的。它也经常被用在一个指令上面,来进行配置。一个value service有点像是一个factory service的缩小版,它经常用来保存值但是我们不能在其中对值进行计算。

我们可以使用angular对象的extend方法来改变一个value service:

app = angular.module("app", []);app.controller(‘MainCtrl‘, function($scope, fooConfig) {  $scope.fooConfig = fooConfig;  angular.extend(fooConfig, {config3: "I have been extended"});});app.value(‘fooConfig‘, {  config1: true,  config2: "Default config2 but it can changes"});   

  

Factory

例子:

app.factory(‘foo‘, function() {  var thisIsPrivate = "Private";  function getPrivate() {    return thisIsPrivate;  }  return {    variable: "This is public",    getPrivate: getPrivate  };});

// or..

app.factory(‘bar‘, function(a) {  return a * 2;});    

Factory service是最普遍使用的service。它同样也非常容易理解。

一个Factory是一个能够返回任何数据类型的service。对于你如何创建它并没有什么可选项,你仅仅需要在其中返回一些东西即可。

正如前面所说的,所有的service类型都是单体,因此如果我们在一个地方修改了foo.variable,其他的地方也会相应的发生改变。

Service

例子:

app.service(‘foo‘, function() {  var thisIsPrivate = "Private";  this.variable = "This is public";  this.getPrivate = function() {    return thisIsPrivate;  };});  

Service service 和 factory差不多。它们之间的区别在于service会接收一个构造器,因此当你第一次使用它的时候,它将会自动运行new Foo()来实例化一个对象。一定要记住如果你在其他的地方也使用了这个service,它将返回同一个对象。

事实上,上面的代码和下面的代码等价:

app.factory(‘foo2‘, function() {  return new Foobar();});function Foobar() {  var thisIsPrivate = "Private";  this.variable = "This is public";  this.getPrivate = function() {    return thisIsPrivate;  };}   

 

Foobar是一个类,我们在首次使用它的时候在我们的factory中将它实例化然后将它返回。和service一样,Foobar将只会实例化一次然后下次当我们再次使用factory时它将返回同一个实例。

如果我们已经有了一个类,并且我们想将它用在service中,我们只需要编写如下的代码:

app.service(foo3,Foobar);   

Provider

Provider是factory的加强版。事实上,上一个例子中的factory代码等价于下面的provider代码:

app.provider(‘foo‘, function() {  return {    $get: function() {      var thisIsPrivate = "Private";      function getPrivate() {        return thisIsPrivate;      }      return {        variable: "This is public",        getPrivate: getPrivate      };    }  };});   

一个provider中应当由一个$get函数,其中的内容就是我们想要注入我们应用中的部分,因此当我们将foo注入一个控制器时,我们实际上注入的是$get函数。

既然factory如此简单,那我们为什么还要使用provider呢?因为我们可以在config阶段配置一个provider。因此我们可以编写下面的代码:

app.provider(‘foo‘, function() {  var thisIsPrivate = "Private";  return {    setPrivate: function(newVal) {      thisIsPrivate = newVal;    },    $get: function() {      function getPrivate() {        return thisIsPrivate;      }      return {        variable: "This is public",        getPrivate: getPrivate      };    }  };});app.config(function(fooProvider) {  fooProvider.setPrivate(‘New value from config‘);});   

在这里我们将thisIsPrivate移到了我们的$get函数的外面,然后我们创建了一个setPrivate来在一个config函数中修改thisIsPrivate。为什么我们需要这样做?这难道不比在factory中添加setter要容易吗?除此之外,还有另外一个原因。

我们想要注入一个特定的对象但是我们想要提供一种方式来根据我们的需求进行一些配置。例如:一个service包含了一个使用jsonp的资源,我们想要配置具体使用的URL,或者我们想要使用一个第三方的service比如restangular来允许我们根据我们的需求来进行配置。

要注意到我们在config函数中放入的是nameProvider而不是name。在这里,我们实际上还是对name进行配置。

看到这里我们其实已经意识到了我们已经在应用中进行过一些配置了,像是$routeProvider以及$locationProvider,两者分别用来配置我们的路由了html5模式。

Decorator

那么现在已经决定要使用前面的 foo service,但是其中还是缺少一个你想要的greet函数。你可以修改factory吗?答案是不行!但是你可以装饰它:

app.config(function($provide){    $provide.decorator(‘foo‘,function($delegate){        $delegate.greet = function(){            return "Hello, I am a new function of ‘foo‘";        }    });});   

$provide是Angular用来在内部创建我们的service的东西。如果我们想要使用它的话可以手动来使用它或者仅仅使用在我们的模块中提供的函数(我们需要使用$provide来进行装饰)。$provide有一个函数,decorator,它让我们可以装饰我们的service。它接收我们想要装饰的service的名字并且在回调函数中接收一个$delegate来代表我们实际上的service实例。

在这里我们可以做一切我们想要的事情来装饰我们的service。在上面的例子中,我们为我们原来的service添加了一个greet函数。接着我们返回了修改后的service。

经过修改以后,现在我们的factory中已经有了一个叫做greet的函数。

装饰一个service的能力是非常实用的,尤其是当我们想要使用第三方的service时,此时我们不需要将代码复制到我们的项目中,而只需要进行一些修改即可。

注意:constant service不能被装饰。

创建一个实例

我们的services都是单体但是我们可以创建一个单体factory来创建新的实例。在你深入之前,记住Angular中的服务都是单体并且我们不想改变这一点。但是,在极少数的情况下你需要生成一个新的实例,你可以像下面这样做:

//我们的类   function Person(json){    angular.extend(this,json);}Person.prototype = {    update: function(){        //更新内容           this.name = "Dave";        this.country = "Canada";    }};   Person.getById = function(id){    //由id来获取一个Person的信息       return new Person({        name: "Jesus",        country: "Spain"        });};//我们的factory   app.factory(‘personService‘,function(){    return {        getById: Person.getById    };});   

在这里我们创建了一个Person对象,它接收一些json数据来初始化对象。然后我们在我们的原型(原型中的函数可以被Person的实例所用)中创建了一个函数,并且在Person上直接创建了一个函数(就像是类函数一样)。

因此现在我们拥有了一个类函数,它将基于我们提供的id来创建一个新的Person对象,并且每一个对象都可以自我更新。现在我们仅仅需要创建一个能够使用它的service。

当每次我们调用personService.getById时,我们都在创建一个新的Person对象,因此你可以在不同的控制器中使用这个service,即便当factory是一个单体,它也能生成新的对象。

总结

Service是Angular中最酷的特性之一。我们可以使用很多方法来创造它们,我们仅仅需要找到符合我们需求的方法然后实现它。

 

本文译自understaning service types,原文地址 http://angular-tips.com/blog/2013/08/understanding-service-types/

理解AngularJS中的Service类型