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

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

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

Constant

示例:

  1. app.constant('fooConfig', {
  2.  
  3. config1: true,
  4.  
  5. config2: "Default config2"
  6.  
  7. });

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

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

HTML:

  1. <!DOCTYPE html>
  2. <html ng-app="app">
  3. <head>
  4. <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
  5. <meta charset=utf-8 />
  6. <title>JS Bin</title>
  7. </head>
  8. <body ng-controller="MainCtrl">
  9. config1: {{fooConfig.config1}}
  10. <br />
  11. config2: {{fooConfig.config2}}
  12. </body>
  13. </html>

JS:

  1. app = angular.module("app", []);
  2.  
  3. app.controller('MainCtrl', function($scope, fooConfig) {
  4. $scope.fooConfig = fooConfig;
  5. });
  6.  
  7. app.constant('fooConfig', {
  8. config1: true,
  9. config2: "Default config2"
  10. });

Value

示例:

  1. app.value('fooConfig', {
  2.  
  3. config1: true,
  4.  
  5. config2: "Default config2 but it can changes"
  6.  
  7. });

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

HTML:

  1. <!DOCTYPE html>
  2. <html ng-app="app">
  3. <head>
  4. <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
  5. <meta charset=utf-8 />
  6. <title>JS Bin</title>
  7. </head>
  8. <body ng-controller="MainCtrl">
  9. config1: {{fooConfig.config1}}
  10. <br />
  11. config2: {{fooConfig.config2}}
  12. <br />
  13. config3: {{fooConfig.config3}}
  14. </body>
  15. </html>

JS:

  1. app = angular.module("app", []);
  2.  
  3. app.controller('MainCtrl', function($scope, fooConfig) {
  4. $scope.fooConfig = fooConfig;
  5. angular.extend(fooConfig, {config3: "I have been extended"});
  6. });
  7.  
  8. app.value('fooConfig', {
  9. config1: true,
  10. config2: "Default config2 but it can changes"
  11. });

Factory

示例:

  1. app.factory('foo', function() {
  2.  
  3. var thisIsPrivate = "Private";
  4.  
  5. function getPrivate() {
  6.  
  7. return thisIsPrivate;
  8.  
  9. }
  10.  
  11. return {
  12.  
  13. variable: "This is public",
  14.  
  15. getPrivate: getPrivate
  16.  
  17. };
  18.  
  19. });

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

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

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

HTML:

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

JS:

  1. app = angular.module("app", []);
  2.  
  3. app.controller('MainCtrl', function($scope, foo) {
  4. $scope.foo = foo;
  5. });
  6.  
  7. app.factory('foo', function() {
  8. var thisIsPrivate = "Private";
  9. function getPrivate() {
  10. return thisIsPrivate;
  11. }
  12.  
  13. return {
  14. variable: "This is public",
  15. getPrivate: getPrivate
  16. };
  17. });

Service

示例:

  1. app.service('foo', function() {
  2.  
  3. var thisIsPrivate = "Private";
  4.  
  5. this.variable = "This is public";
  6.  
  7. this.getPrivate = function() {
  8.  
  9. return thisIsPrivate;
  10.  
  11. };
  12.  
  13. });

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

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

  1. app.factory('foo2', function() {
  2.  
  3. return new Foobar();
  4.  
  5. });
  6.  
  7. function Foobar() {
  8.  
  9. var thisIsPrivate = "Private";
  10.  
  11. this.variable = "This is public";
  12.  
  13. this.getPrivate = function() {
  14.  
  15. return thisIsPrivate;
  16.  
  17. };
  18.  
  19. }

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

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

  1. app.service('foo3', Foobar);

HTML:

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

JS:

  1. app = angular.module("app", []);
  2.  
  3. app.controller('MainCtrl', function($scope, foo) {
  4. $scope.foo = foo;
  5. });
  6.  
  7. app.service('foo', function() {
  8. var thisIsPrivate = "Private";
  9. this.variable = "This is public";
  10. this.getPrivate = function() {
  11. return thisIsPrivate;
  12. };
  13. });
  14.  
  15. // This is the same as the service.
  16. app.factory('foo2', function() {
  17. return new Foobar();
  18. });
  19.  
  20. function Foobar() {
  21. var thisIsPrivate = "Private";
  22. this.variable = "This is public";
  23. this.getPrivate = function() {
  24. return thisIsPrivate;
  25. };
  26. }
  27.  
  28. // Or even this
  29. app.service('foo3', Foobar);

Provider

Provider 在angular中是个最终的高级选项,在上例factory中最后一个示例用provider将是如下:

  1. app.provider('foo', function() {
  2.  
  3. return {
  4.  
  5. $get: function() {
  6.  
  7. var thisIsPrivate = "Private";
  8.  
  9. function getPrivate() {
  10.  
  11. return thisIsPrivate;
  12.  
  13. }
  14.  
  15. return {
  16.  
  17. variable: "This is public",
  18.  
  19. getPrivate: getPrivate
  20.  
  21. };
  22.  
  23. }
  24.  
  25. };
  26.  
  27. });

provider带有一个$get的函数,其返回值将会被注入其他应用组件。所以我们注入foo到controller,我们注入的是$get 函数。

为什么我们还需要provider,factory实现不是更简单吗?这是因为我们能够在config 函数中配置provider。如下所示:

  1. app.provider('foo', function() {
  2.  
  3. var thisIsPrivate = "Private";
  4.  
  5. return {
  6.  
  7. setPrivate: function(newVal) {
  8.  
  9. thisIsPrivate = newVal;
  10.  
  11. },
  12.  
  13. $get: function() {
  14.  
  15. function getPrivate() {
  16.  
  17. return thisIsPrivate;
  18.  
  19. }
  20.  
  21. return {
  22.  
  23. variable: "This is public",
  24.  
  25. getPrivate: getPrivate
  26.  
  27. };
  28.  
  29. }
  30.  
  31. };
  32.  
  33. });
  34.  
  35. app.config(function(fooProvider) {
  36.  
  37. fooProvider.setPrivate('New value from config');
  38.  
  39. });

在这里我们把thisISPrivate移出了$get函数,我们创建了一个setPrivate函数,使其能够在config函数中修改thisIsPrivate变量。为什么我们需要这么做?在factory中加入一个setter不就好了吗?这是一个不同的意图。

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

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

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

HTML:

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

JS:

  1. app = angular.module("app", []);
  2.  
  3. app.controller('MainCtrl', function($scope, foo) {
  4. $scope.foo = foo;
  5. });
  6.  
  7. app.provider('foo', function() {
  8.  
  9. var thisIsPrivate = "Private";
  10.  
  11. return {
  12.  
  13. setPrivate: function(newVal) {
  14. thisIsPrivate = newVal;
  15. },
  16.  
  17. $get: function() {
  18. function getPrivate() {
  19. return thisIsPrivate;
  20. }
  21.  
  22. return {
  23. variable: "This is public",
  24. getPrivate: getPrivate
  25. };
  26. }
  27.  
  28. };
  29.  
  30. });
  31.  
  32. app.config(function(fooProvider) {
  33. fooProvider.setPrivate('New value from config');
  34. });

额外的福利:

福利1:装潢器Decorator

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

  1. app.config(function($provide) {
  2.  
  3. $provide.decorator('foo', function($delegate) {
  4.  
  5. $delegate.greet = function() {
  6.  
  7. return "Hello, I am a new function of 'foo'";
  8.  
  9. };
  10.  
  11. return $delegate;
  12.  
  13. });
  14.  
  15. });

上例中$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:

  1. <!DOCTYPE html>
  2. <html ng-app="app">
  3. <head>
  4. <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
  5. <meta charset=utf-8 />
  6. <title>JS Bin</title>
  7. </head>
  8. <body ng-controller="MainCtrl">
  9. public variable: {{foo.variable}}
  10. <br />
  11. private variable (through getter): {{foo.getPrivate()}}
  12. <br />
  13. greet: {{foo.greet()}}
  14. </body>
  15. </html>

JS:

  1. app = angular.module("app", []);
  2.  
  3. app.controller('MainCtrl', function($scope, foo) {
  4. $scope.foo = foo;
  5. });
  6.  
  7. app.factory('foo', function() {
  8. var thisIsPrivate = "Private";
  9. function getPrivate() {
  10. return thisIsPrivate;
  11. }
  12.  
  13. return {
  14. variable: "This is public",
  15. getPrivate: getPrivate
  16. };
  17. });
  18.  
  19. app.config(function($provide) {
  20. $provide.decorator('foo', function($delegate) {
  21. $delegate.greet = function() {
  22. return "Hello, I am a new function of 'foo'";
  23. };
  24.  
  25. return $delegate;
  26. });
  27. });

福利2:创建非单例对象

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

  1. // Our class
  2.  
  3. function Person( json ) {
  4.  
  5. angular.extend(this, json);
  6.  
  7. }
  8.  
  9. Person.prototype = {
  10.  
  11. update: function() {
  12.  
  13. // Update it (With real code :P)
  14.  
  15. this.name = "Dave";
  16.  
  17. this.country = "Canada";
  18.  
  19. }
  20.  
  21. };
  22.  
  23. Person.getById = function( id ) {
  24.  
  25. // Do something to fetch a Person by the id
  26.  
  27. return new Person({
  28.  
  29. name: "Jesus",
  30.  
  31. country: "Spain"
  32.  
  33. });
  34.  
  35. };
  36.  
  37. // Our factory
  38.  
  39. app.factory('personService', function() {
  40.  
  41. return {
  42.  
  43. getById: Person.getById
  44.  
  45. };
  46.  
  47. });

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

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

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

HTML:

  1. <!DOCTYPE html>
  2. <html ng-app="app">
  3. <head>
  4. <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
  5. <meta charset=utf-8 />
  6. <title>JS Bin</title>
  7. </head>
  8. <body>
  9. <p ng-controller="MainCtrl">{{aPerson.name}} - {{aPerson.country}}</p>
  10. <div ng-controller="SecondCtrl">
  11. {{aPerson.name}} - {{aPerson.country}}
  12. <button ng-click="updateIt()">Update it</button>
  13. </div>
  14. </body>
  15. </html>

JS:

  1. app = angular.module('app', []);
  2.  
  3. app.controller('MainCtrl', function($scope, personService) {
  4. $scope.aPerson = Person.getById(1);
  5. });
  6.  
  7. app.controller('SecondCtrl', function($scope, personService) {
  8. $scope.aPerson = Person.getById(1);
  9.  
  10. $scope.updateIt = function() {
  11. $scope.aPerson.update();
  12. };
  13. });
  14.  
  15. // Our class
  16. function Person( json ) {
  17. angular.extend(this, json);
  18. }
  19.  
  20. Person.prototype = {
  21. update: function() {
  22. // Update it (With real code :P)
  23. this.name = "Dave";
  24. this.country = "Canada";
  25. }
  26. };
  27.  
  28. Person.getById = function( id ) {
  29. // Do something to fetch a Person by the id
  30. return new Person({
  31. name: "Jesus",
  32. country: "Spain"
  33. });
  34. };
  35.  
  36. // Our factory
  37. app.factory('personService', function() {
  38. return {
  39. getById: Person.getById
  40. };
  41. });

福利3:CoffeeScript

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

  1. app.controller 'MainCtrl', ($scope, personService) ->
  2.  
  3. $scope.aPerson = personService.getById(1)
  4.  
  5. app.controller 'SecondCtrl', ($scope, personService) ->
  6.  
  7. $scope.aPerson = personService.getById(2)
  8.  
  9. $scope.updateIt = () ->
  10.  
  11. $scope.aPerson.update()
  12.  
  13. class Person
  14.  
  15. constructor: (json) ->
  16.  
  17. angular.extend @, json
  18.  
  19. update: () ->
  20.  
  21. @name = "Dave"
  22.  
  23. @country = "Canada"
  24.  
  25. @getById: (id) ->
  26.  
  27. new Person
  28.  
  29. name: "Jesus"
  30.  
  31. country: "Spain"
  32.  
  33. app.factory 'personService', () ->
  34.  
  35. {
  36.  
  37. getById: Person.getById
  38.  
  39. }

HTML:

  1. <!DOCTYPE html>
  2. <html ng-app="app">
  3. <head>
  4. <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
  5. <meta charset=utf-8 />
  6. <title>JS Bin</title>
  7. </head>
  8. <body>
  9. <p ng-controller="MainCtrl">{{aPerson.name}} - {{aPerson.country}}</p>
  10. <div ng-controller="SecondCtrl">
  11. {{aPerson.name}} - {{aPerson.country}}
  12. <button ng-click="updateIt()">Update it</button>
  13. </div>
  14. </body>
  15. </html>

JS:

  1. app = angular.module 'app', []
  2.  
  3. app.controller 'MainCtrl', ($scope, personService) ->
  4. $scope.aPerson = personService.getById(1)
  5.  
  6. app.controller 'SecondCtrl', ($scope, personService) ->
  7. $scope.aPerson = personService.getById(2)
  8. $scope.updateIt = () ->
  9. $scope.aPerson.update()
  10.  
  11. class Person
  12.  
  13. constructor: (json) ->
  14. angular.extend @, json
  15.  
  16. update: () ->
  17. @name = "Dave"
  18. @country = "Canada"
  19.  
  20. @getById: (id) ->
  21. new Person
  22. name: "Jesus"
  23. country: "Spain"
  24.  
  25. app.factory 'personService', () ->
  26. {
  27. getById: Person.getById
  28. }

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

总结:

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

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

AngularJS 之Services讲解的更多相关文章

  1. [AngularJS] Accessing Services from Console

    Using the Chrome console, you can access your AngularJS injectable services. This is down and dirty ...

  2. AngularJS Moudle 函数讲解

    AngularJS中的Module类负责定义应用如何启动,它还可以通过声明的方式定义应用中的各个片段.我们来看看它是如何实现这些功能的. 一.Main方法在哪里 如果你是从Java或者Python编程 ...

  3. [AngularJS] Using Services in Angular Directives

    Directives have dependencies too, and you can use dependency injection to provide services for your ...

  4. angularjs directive 实例 详解

    前面提到了angularjs的factory,service,provider,这个可以理解成php的model,这种model是不带html的,今天所说的directive,也可以理解成php的mo ...

  5. Decoration2:引入Angularjs显示前台一条数据

    SpringMVC内置的RestFul API格式采用的是最复杂最全面的HATEOAS规范,对于简单应用来说,前台解析起来不方便,我们下面主要想办法重新定义一种简单的RestFulAPI. (1)先是 ...

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

    极客WEB大前端专家级开发工程师培训视频教程  教程下载地址: http://www.fu83.cn/thread-355-1-1.html 课程目录:1.走进前端工程师的世界HTML51.HTML5 ...

  7. web 前端开发学习路线

    初级 HTML 5 HTML 5 与 HTML 4 的区别 HTML 5 新增的主体结构元素 HTML 5 新增的非主体结构元素 HTML 5 表单新增元素与属性 HTML 5 表单新增元素与属性(续 ...

  8. 2014年GDG西安 -- DevFest Season1

    今年9月21日,GDG西安组织了第一季以Android Wear为专题的活动,葡萄城则以超一流的办公环境和网络宣传,配合举行了本次活动.下面通过图文方式进行报道,希望未能如期参加的筒子们不要有太多的遗 ...

  9. 指令-Directive

    restrict:'A'用作设定用那种方式使用指令. 可组合使用如restrict:'AE' E - 元素名称: <my-directive></my-directive> A ...

随机推荐

  1. C++ Primer Plus 6th 读书笔记 - 第6章 分支语句和逻辑运算符

    1. cin读取错误时对换行符的处理 #include <iostream> using namespace std; int main() { double d; char c; cin ...

  2. (原)ubuntu16中安装moses

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5653186.html 在ubuntu14中,可以使用下面的语句安装moses: luarocks in ...

  3. [C++程序设计]用指向数组的指针作函数参数

    #include <iostream> using namespace std; int main() { ]); ][]={,,,,,,,,,,,}; output(a); ; } ]) ...

  4. 在Cocos2d-X中新建Android项目

    Windows下创建Cocos2d-X的Android项目并不复杂,关键是要改几个环境变量 一.进入Cocos2d-X主目录修改“create-android-project.bat” 大家都知道要点 ...

  5. FPGA系统中DRAM,SRAM,SDRAM,FLASH 区别(转)

    原文:http://hi.baidu.com/abners/item/a9042ef35d3f005bc8f337f5 一般来说这几种存储器是一个nios系统都具有的,sram的好处是接口简单,速度快 ...

  6. Mysql基本类型(字符串类型)——mysql之二

    转自: http://www.cnblogs.com/doit8791/archive/2012/05/28/2522556.html 1.varchar类型的变化 MySQL 数据库的varchar ...

  7. Ubuntu14.04LST安装weblogic11g

    1:下载链接http://download.oracle.com/otn/nt/middleware/11g/wls/1036/wls1036_generic.jar 2:进行安装(前提已经安装好JD ...

  8. hdu 1428 漫步校园

    http://acm.hdu.edu.cn/showproblem.php?pid=1428 dijstra+dp; #include <cstdio> #include <queu ...

  9. bzoj1650 [Usaco2006 Dec]River Hopscotch 跳石子

    Description Every year the cows hold an event featuring a peculiar version of hopscotch that involve ...

  10. c语言中内存对齐问题

    在最近的项目中,我们涉及到了“内存对齐”技术.对于大部分程序员来说,“内存对齐”对他们来说都应该是“透明的”.“内存对齐”应该是编译器的“管辖范围”.编译器为程序中的每个“数据单元”安排在适当的位置上 ...