zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    Neo4j 第五篇:批量更新数据
    Neo4j 第四篇:使用.NET驱动访问Neo4j
    Neo4j 第三篇:Cypher查询入门
    Neo4j 第二篇:图形数据库
    Neo4j 第一篇:在Windows环境中安装Neo4j
    ElasticSearch入门 第九篇:实现正则表达式查询的思路
    ElasticSearch入门 第八篇:存储
    ElasticSearch入门 第七篇:分词
    ElasticSearch入门 第六篇:复合数据类型——数组,对象和嵌套
    Package 设计3:数据源的提取和使用暂存
  • 原文地址:https://www.cnblogs.com/lodingzone/p/4846158.html
Copyright © 2011-2022 走看看