AngularJS 之Services讲解

Angular带来了很多类型的services。每个都会它自己不同的使用场景。我们将在本节来阐述。

首先我们必须记在心里的是所有的services都是singleton(单例)的,这也是我们所希望得到的预期结果。

下面让我开始今天的services之旅吧:

Constant

示例:

app.constant(‘fooConfig‘, {

config1: true,

config2: "Default config2"

});

constant是个很有用的东东,我们经常会用于对directive之类的做配置信息。所以当你想创建一个directive,并且你希望能够做一些配置信息,同时给些默认的配置,constant是个不错的的选择。

constant可以译作常量,因为我们所设置的值value是不能被改变的。其可以接受基础类型和object对象。

HTML:

<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
  <body ng-controller="MainCtrl">
    config1: {{fooConfig.config1}}
    <br />
    config2: {{fooConfig.config2}}
  </body>
</html>

JS:

app = angular.module("app", []);

app.controller(‘MainCtrl‘, function($scope, fooConfig) {
  $scope.fooConfig = fooConfig;
});

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

Value

示例:

app.value(‘fooConfig‘, {

config1: true,

config2: "Default config2 but it can changes"

});

Value和上面的constant很相似,唯一是其在赋值后还可以被改变。它也被常用于directive配置信息。Value service只会保留values,我们不会在service中计算其值。

HTML:

<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
  <body ng-controller="MainCtrl">
    config1: {{fooConfig.config1}}
    <br />
    config2: {{fooConfig.config2}}
    <br />
    config3: {{fooConfig.config3}}
  </body>
</html>

JS:

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

};

});

Factory是我们最常用的service。其很容易被理解。

factory会返回一个object对象,至于你如何创建这个对象angular没任何限制。在示例中我选择了我喜欢的模式 Revealing module pattern,你可以选择其他你所希望的方式。

如我之前所说,所有的services都是singleton的,所以当我们修改foo.variable的时候,会影响到其他使用的地方。

HTML:

<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
  <body ng-controller="MainCtrl">
    public variable: {{foo.variable}}
    <br />
    private variable (through getter): {{foo.getPrivate()}}
  </body>
</html>

JS:

app = angular.module("app", []);

app.controller(‘MainCtrl‘, function($scope, foo) {
  $scope.foo = foo;
});

app.factory(‘foo‘, function() {
  var thisIsPrivate = "Private";
  function getPrivate() {
    return thisIsPrivate;
  }

  return {
    variable: "This is public",
    getPrivate: getPrivate
  };
});

Service

示例:

app.service(‘foo‘, function() {

var thisIsPrivate = "Private";

this.variable = "This is public";

this.getPrivate = function() {

return thisIsPrivate;

};

});

Service service和factory工作原理一样,只是他service接收的是一个构造函数,当第一次使用service的时候,angular会new Foo() 来初始化这个对象。以后的时候返回的都是同一个对象。

实际上,下面是factory等价的写法:

app.factory(‘foo2‘, function() {

return new Foobar();

});

function Foobar() {

var thisIsPrivate = "Private";

this.variable = "This is public";

this.getPrivate = function() {

return thisIsPrivate;

};

}

Foobar是一个class(类)在factory中我们手动初始化它,在返回它。和service一样Foobar class只在第一次初始化,并以后返回的都是同一个对象。

如果我们已经存在了一个class,那么我可以直接使用:

app.service(‘foo3‘, Foobar);

HTML:

<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
  <body ng-controller="MainCtrl">
    public variable: {{foo.variable}}
    <br />
    private variable (through getter): {{foo.getPrivate()}}
  </body>
</html>

JS:

app = angular.module("app", []);

app.controller(‘MainCtrl‘, function($scope, foo) {
  $scope.foo = foo;
});

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

// This is the same as the service.
app.factory(‘foo2‘, function() {
  return new Foobar();
});

function Foobar() {
  var thisIsPrivate = "Private";
  this.variable = "This is public";
  this.getPrivate = function() {
    return thisIsPrivate;
  };
}

// Or even this
app.service(‘foo3‘, Foobar);

Provider

Provider 在angular中是个最终的高级选项,在上例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到controller,我们注入的是$get 函数。

为什么我们还需要provider,factory实现不是更简单吗?这是因为我们能够在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不就好了吗?这是一个不同的意图。

我们希望注入的是一个object对象,但是我们也希望能够提供一种方式去配置它。例如:一个包装了jsonp的资源resource的service,我们希望能够配置是从那个url获取资源,我们也将有个三方的消费者比如restangular允许我们去配置达到我们的目的。

注意在config函数我们需要用nameProvider替代name,然而消费者只需要用name。

可以看见在在我们的应用程序中已经配置了一些services,比如$routeProvider,$locationProvider,配置我们的routes和html5model 调整适应。

HTML:

<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
  <body ng-controller="MainCtrl">
    public variable: {{foo.variable}}
    <br />
    private variable (through getter): {{foo.getPrivate()}}
  </body>
</html>

JS:

app = angular.module("app", []);

app.controller(‘MainCtrl‘, function($scope, foo) {
  $scope.foo = foo;
});

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‘);
});

额外的福利:

福利1:装潢器Decorator

如果你觉得我给你的foo service缺少了你所需要的greet方法,你需要改变API吗?不,你可以用更好的方法装潢:

app.config(function($provide) {

$provide.decorator(‘foo‘, function($delegate) {

$delegate.greet = function() {

return "Hello, I am a new function of ‘foo‘";

};

return $delegate;

});

});

上例中$provide是angular内部用于创建我们所有service的service。如果我们希望在我们的应用程序中使用,我们可以手动的使用它(我们可以用$provide去装潢)。$provide有一个decorator的装潢函数,允许我们装潢我们的services,它接受我们所需要装潢的service的name和一个接受$delegate的回调函数,$delegate代表我们的原来的service实例。

在这里我们可以装潢我们的service。在本例中我们在原来的service实例上增加了一个greet函数,在返回修改过后的service。当我们消费这个service的时候,它将会包含一个greet的函数,你可以在下面的try it中看见。

对于使用来自第三方的service,当我们期望对其接口做一些扩展的时候,我们不需要copy它的代码到我们的项目来修改它,我们可以手动方便的使用装潢器去实现我们所想要的。

注意:上文中说的常量constant是不可以被装潢的。

HTML:

<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
  <body ng-controller="MainCtrl">
    public variable: {{foo.variable}}
    <br />
    private variable (through getter): {{foo.getPrivate()}}
    <br />
    greet: {{foo.greet()}}
  </body>
</html>

JS:

app = angular.module("app", []);

app.controller(‘MainCtrl‘, function($scope, foo) {
  $scope.foo = foo;
});

app.factory(‘foo‘, function() {
  var thisIsPrivate = "Private";
  function getPrivate() {
    return thisIsPrivate;
  }

  return {
    variable: "This is public",
    getPrivate: getPrivate
  };
});

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

    return $delegate;
  });
});

福利2:创建非单例对象

如我们所知所有的service都是单例的,但是我们仍然可以创建一个非单例的对象。在我们深入之前,我们必须认识到大多数场景我们都会期望是个单例的service,我们也不会去改变这种机制。换句话在很少的场景中我们需要每次生成一个新的object对象。如下:

// Our class

function Person( json ) {

angular.extend(this, json);

}

Person.prototype = {

update: function() {

// Update it (With real code :P)

this.name = "Dave";

this.country = "Canada";

}

};

Person.getById = function( id ) {

// Do something to fetch a Person by the id

return new Person({

name: "Jesus",

country: "Spain"

});

};

// Our factory

app.factory(‘personService‘, function() {

return {

getById: Person.getById

};

});

在这里我们创建了一个Person对象,它几首一些json对象来初始化对象。接下来我们在prototype中创建了一个函数(可以从面向对象语言理解为实例方法),在我们直接在Person类上加了一个方法(可以理解为类方法,静态方法)。

所以我们有一个类方法将根据我们提供的id创建一个新的person对象,并每隔实例可以更新自己。接下来我们只需要创建一个service去消费它。

在任何时候我们调用personService.getByID,我们都会创建一个新的person对象,所以在不同的controller中你可以使用一份新的person对象,即使factory是单例的,但是它生产返回的却是新的object。

HTML:

<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
  <body>
    <p ng-controller="MainCtrl">{{aPerson.name}} - {{aPerson.country}}</p>
    <div ng-controller="SecondCtrl">
      {{aPerson.name}} - {{aPerson.country}}
      <button ng-click="updateIt()">Update it</button>
    </div>
  </body>
</html>

JS:

app = angular.module(‘app‘, []);

app.controller(‘MainCtrl‘, function($scope, personService) {
  $scope.aPerson = Person.getById(1);
});

app.controller(‘SecondCtrl‘, function($scope, personService) {
  $scope.aPerson = Person.getById(1);

  $scope.updateIt = function() {
    $scope.aPerson.update();
  };
});

// Our class
function Person( json ) {
  angular.extend(this, json);
}

Person.prototype = {
  update: function() {
    // Update it (With real code :P)
    this.name = "Dave";
    this.country = "Canada";
  }
};

Person.getById = function( id ) {
  // Do something to fetch a Person by the id
  return new Person({
    name: "Jesus",
    country: "Spain"
  });
};

// Our factory
app.factory(‘personService‘, function() {
  return {
    getById: Person.getById
  };
});

福利3:CoffeeScript

CoffeeScrip能够方便优雅的处理service,提供的优雅的方式去创建class。下面是福利2的示例用CoffeeScript改变后的:

app.controller ‘MainCtrl‘, ($scope, personService) ->

$scope.aPerson = personService.getById(1)

app.controller ‘SecondCtrl‘, ($scope, personService) ->

$scope.aPerson = personService.getById(2)

$scope.updateIt = () ->

$scope.aPerson.update()

class Person

constructor: (json) ->

angular.extend @, json

update: () ->

@name = "Dave"

@country = "Canada"

@getById: (id) ->

new Person

name: "Jesus"

country: "Spain"

app.factory ‘personService‘, () ->

{

getById: Person.getById

}

HTML:

<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
    <p ng-controller="MainCtrl">{{aPerson.name}} - {{aPerson.country}}</p>
    <div ng-controller="SecondCtrl">
      {{aPerson.name}} - {{aPerson.country}}
      <button ng-click="updateIt()">Update it</button>
    </div>
  </body>
</html>

JS:

app = angular.module ‘app‘, []

app.controller ‘MainCtrl‘, ($scope, personService) ->
  $scope.aPerson = personService.getById(1)

app.controller ‘SecondCtrl‘, ($scope, personService) ->
  $scope.aPerson = personService.getById(2)
  $scope.updateIt = () ->
    $scope.aPerson.update()

class Person

  constructor: (json) ->
    angular.extend @, json

  update: () ->
    @name = "Dave"
    @country = "Canada"

  @getById: (id) ->
    new Person
      name: "Jesus"
      country: "Spain"

app.factory ‘personService‘, () ->
  {
    getById: Person.getById
  }

译者注:本人一直在思考一篇《为什么需要在你的项目中尝试CoffeeScript》.CoffeeScript不仅仅优美语法,如果只是这样的话,充其量这也只是一些可有可无的语法糖而已,我们认为更重要的是它为我们写javascript带来了一些好的实践,规避了javascript的“坑”.但是也不得不考虑项目成员学习成本,如果你项目成员很多具有函数式编程的经历,javascript能力也不错,你完全可以去尝试。注:写CoffeeScript并不是说你不再需要javascript学习。

总结:

service是angularjs另一个非常酷的features。我们有许多方式去创建service,我们需要根据我们的应用场景选择正确的方式去实现它。译者注:这就好比我们常挂在嘴边的设计模式,重要的是正确的场景使用正确的模式。

本来原文来自:http://angular-tips.com/blog/2013/08/understanding-service-types/

时间: 2024-12-14 23:17:36

AngularJS 之Services讲解的相关文章

[AngularJS] Accessing Services from Console

Using the Chrome console, you can access your AngularJS injectable services. This is down and dirty debugging, and can be a lifesaver in troubling times. You can get services/factories in console by using: var $injector = angular.element($0).injector

[AngularJS] Using Services in Angular Directives

Directives have dependencies too, and you can use dependency injection to provide services for your directives to use. Bad: If you want to use <alert> in another controller or page, you have to modify the AlertService. This might break things. <!

angularjs directive 实例 详解

前面提到了angularjs的factory,service,provider,这个可以理解成php的model,这种model是不带html的,今天所说的directive,也可以理解成php的model,也可以理解成插件,只不过这种model是带html的,例如:php的分页函数. 一,angularjs directive的常用格式,以及参数说明 1,直接return phonecatDirectives = angular.module('phonecatDirectives', [])

[web建站] 极客WEB大前端专家级开发工程师培训视频教程

极客WEB大前端专家级开发工程师培训视频教程  教程下载地址: http://www.fu83.cn/thread-355-1-1.html 课程目录:1.走进前端工程师的世界HTML51.HTML5与HTML4的区别2.HTML5新增的主体结构元素3.HTML5新增的的非主体结构元素 4.HTML5表单新增元素与属性5.HTML5表单新增元素与属性(续)6.HTML5改良的input元素的种类 7.HTML5增强的页面元素8.HTML5编辑API之Range对象(一)9.HTML5编辑API之

极客学院Web前端开发技术实战视频教程 初级入门+高级实战++专家课程+面试指导

===============课程目录=============== ├<初级中级>│  ├<1. HTML5开发前准备>│  │  ├1.HTML5开发前准备.mp4│  │  └2.开发前的准备-快捷键.mp4│  ├<10. React.js>│  │  ├React.js简介.txt│  │  ├<1.React 概述>│  │  │  ├React 开发环境搭建.mp4│  │  │  ├编写第一个 React 程序.mp4│  │  │  └什么

Angular1 Attack!!!

1.构建一个CRUD(create retrieve update delete)应用需要用到什么? 数据绑定 基本模板标识符 表单验证 路由 深度链接 组件重用 依赖注入 2.运用AngularJs的(Services)和指令(directives)构建完整,健壮的应用. - 模板系统. It uses HTML as the templating language • It doesn't require an explicit DOM refresh, as AngularJS is ca

20个为前端开发者准备的文档和指南

20个为前端开发者准备的文档和指南 来源:codeceo 发布时间:2016-07-27 阅读次数:358 0 是时候重新学习了!和以前一样,我收集了很多不同的学习资源,包括学习指南,学习文档,和其他有用的网站来帮助你在前端开发的不同领域里快速地进入状态. 所以请尽情享受我们的文档和指南系列的第九部分,并且不要忘了在评论区让我知道任何我没有找到的. 1. JavaScript Standard Style(JavaScript标准样式) 链接:http://standardjs.com Java

2014年GDG西安 -- DevFest Season1

?? 今年9月21日,GDG西安组织了第一季以Android Wear为专题的活动,葡萄城则以超一流的办公环境和网络宣传,配合举行了本次活动.下面通过图文方式进行报道,希望未能如期参加的筒子们不要有太多的遗憾呀: Welcome to GrapeCity,Again! 随着金秋时节的来临,又到了一年一度的DevFest季节:葡萄城非常棒的草坪,员工上.下午课间操,夏天可户外烧烤. 葡萄城优雅的办公环境,是一个非常有格调的公司... 现场赠送的礼品,有GlassCup,T恤,Notebook,还会

AngularJS 讲解,四 Directive

AngularJS  Directive 自定义指令(我最喜欢AngularJs的功能之一) 一:什么时候我们会用到directive 1.使html更具语义化,不用深入了解研究代码的逻辑便可知道大致逻辑. 2.抽象出一个自定义组件,可以重复使用. 二:directive的定义及其使用方法   1.下面是一个directive参数详细模板 angular.module('app',[]);//申明一个调用angularjs块 angular.module('app').directive('di