Angular五種服務詳解

(轉自http://www.cnblogs.com/liulangmao/p/4078246.html

在這之前angular學習筆記(十五)-module裏的'服務'這篇文章裏,已經大致講解了ng中的'服務',在之後的很多地方也用到了服務,但是,所有的服務都是使用app.factory來創建的.但其實,創建服務有5種方法,這篇文章就來具體講解ng中的五種服務類型.

首先,爲了舉栗子,先寫好如下的模型,控制器,html:

html:

複製代碼

<!DOCTYPE html>
<html ng-app="serviceApp">
<head>
  <title>服務</title>
  <meta charset="utf-8">
  <script src="../angular.js"></script>
  <script src="script.js"></script>
<!--  <script src="script_2.js"></script>-->
</head>
<body >
  <div ng-controller="myCtrl">
    name:{{name}}
    <br/>
    age:{{age}}
    <br/>
    love:{{love}}
    <br/>
    money:{{money}}
    <br/>
    id:{{id}}
  </div>
  <hr/>
  <div ng-controller="myOtherCtrl">
    name:{{name}}
    <br/>
    love:{{love}}
    <br/>
  </div>
  <hr/>
</body>
</html>

複製代碼

js:

複製代碼

var serviceApp = angular.module('serviceApp',[]);

serviceApp.controller('myCtrl',function($scope,myConfig){
    $scope.name = myConfig.name;
    $scope.love = myConfig.love;
    $scope.age = myConfig.age;
    $scope.money = myConfig.money;
    $scope.id = myConfig.getId();
    $scope.$watch(myConfig.love,function(){$scope.love = myConfig.love;});
})

serviceApp.controller('myOtherCtrl',function($scope,myConfig){
    $scope.name = myConfig.name;
    $scope.love = myConfig.love;
    angular.extend(myConfig,{love:'zxg'});
    $scope.$watch(myConfig.love,function(){$scope.love = myConfig.love;});
});

複製代碼

如上,serviceApp模型裏有兩個控制器,myCtrl和myOtherCtrl,這兩個控制器都注入了myConfig這個服務.我們在後面就通過創建不同的myConfig服務來查看結果

 

一.constant服務:

app.constant('name',obj)

name爲服務的名字,obj爲一個json對象.

js:

複製代碼

serviceApp.constant('myConfig',{
    name:'code_bunny',
    age:12,
    getId:function(){
        return 1
    }
});

複製代碼

運行結果: http://jsfiddle.net/f9qq0t50/1/

說明:

constant創建服務返回一個json對象(也就是第二個參數中傳入的對象),這個對象裏可以有參數,可以有方法,並且,屬性和方法都可以在控制器中修改,新增,但是按照它的設計本意,一般constant創建的服務不會去修改它的內容,需要修改內容,最好用value來創建服務. 

注意點:

1.它是一個引用對象,無論被注入多少個控制器中,實際都指向同一個對象,所以,無論修改其中的哪一個,其它所有的服務都會被改變.

2.服務修改過後,ng不會自動同步,簡單的說,就是它沒有做到自動雙向綁定數據,比如在這裏給服務新增了love屬性:angular.extend(myConfig,{love:'zxg'}),我需要使用$scope.$watch(myConfig.love,function(){$scope.love = myConfig.love;})這樣才能把love屬性值同步到視圖中.

3.constant服務不能通過decorator進行裝飾,(什麼是裝飾下面會講到)

 

二.value服務:

app.value('name',obj)

name爲服務的名字,obj爲一個json對象.

js:

複製代碼

serviceApp.value('myConfig',{
    name:'code_bunny',
    age:12,
    getId:function(){
        return 1
    }
});

複製代碼

serviceApp.config(function($provide){
    $provide.decorator('myConfig',function($delegate){
        $delegate.money = '100w';
        return $delegate
    })
});

運行結果: http://jsfiddle.net/p0dqr7wy/1/

說明:

value創建服務返回一個json對象(也就是第二個參數中傳入的對象),這個對象裏可以有參數,可以有方法,並且,屬性和方法都可以在控制器中修改,新增,按照它的設計本意,如果屬性和方法需要被修改內容,就用value來創建服務. 

constant和value主要就是用於存放一些數據或方法以供使用,區別是constant一般是存放固定內容,value存放可能會被修改的內容

注意點:

1.同constant注意點1

2.同constant注意點2

3.value可以被裝飾,所以這裏myConfig服務擁有了money屬性.(裝飾具體怎麼用,下面會說)

 

三.factory服務

app.factory('name',function(){return obj})

name爲服務的名字,第二個參數傳入一個函數,函數需要有一個返回值obj,返回一個對象.實際被注入的服務就是這個對象.

js:

複製代碼

serviceApp.factory('myConfig',function(){
    var myname = 'code_bunny';
    var age = 12;
    var id = 1;
    return {
        name: myname,
        age: age,
        getId: function(){
            return id
        }
    }
});

複製代碼

或者是這樣:

複製代碼

serviceApp.factory('myConfig',function(){
    return new constructorFun()
});

function constructorFun(){
    var myname = 'code_bunny';
    var age = 12;
    var id = 1;
    this.name = myname;
    this.age = age;
    this.getId = function(){
        return id
    }
}

複製代碼

裝飾部分代碼:

複製代碼

serviceApp.config(function($provide){
    $provide.decorator('myConfig',function($delegate){
        console.log($delegate);
        $delegate.money = '100w';
        return $delegate
    })
});

複製代碼

運行結果:

http://jsfiddle.net/8kuxt3xc/

http://jsfiddle.net/ua2y617q/

說明:

factory服務是最常見最常用的服務類型,幾乎可以滿足90%的自己開發的需求,使用它可以編寫一些邏輯,通過這些邏輯最後返回所需要的對象.比如使用$http來獲取一些數據.我們就在factory創建的服務裏抓取數據,最後返回.

它和constant,value最大的區別是,factory服務是有一個處理過程,經過這個過程,才返回結果的. 

注意點:

1.同constant注意點1

2.同constant注意點2

3.factory返回的服務也可以被裝飾,所以這裏myConfig服務擁有了money屬性.(裝飾具體怎麼用,下面會說)

 

四.service服務

app.service('name',constructor)

name爲服務的名字,constructor是一個構造函數.

js:

複製代碼

serviceApp.service('myConfig',function(){
    var myname = 'code_bunny';
    var age = 12;
    var id = 1;
    this.name = myname;
    this.age = age;
    this.getId = function(){
        return id
    }
});

複製代碼

或者是這樣:

複製代碼

serviceApp.service('myConfig',constructorFun);
function constructorFun(){
    var myname = 'code_bunny';
    var age = 12;
    var id = 1;
    this.name = myname;
    this.age = age;
    this.getId = function(){
        return id
    }
}

複製代碼

裝飾部分代碼同上.

運行結果:

http://jsfiddle.net/1qj8m5ot/

http://jsfiddle.net/0bh67cog/

說明:

service和factory的區別在於,它第二個參數傳入的是一個構造函數,最後被注入的服務是這個構造函數實例化以後的結果.所以基本上使用service創建的服務的,也都可以使用factory來創建.

所以這裏,factory服務的第二種寫法和使用service是一致的:

serviceApp.factory('myConfig',function(){
    return new constructorFun()
});
//等價於
serviceApp.service('myConfig',constructorFun);

注意點:

1.同constant注意點1

2.同constant注意點2

3.service返回的服務也可以被裝飾,所以這裏myConfig服務擁有了money屬性.(裝飾具體怎麼用,下面會說)

 

五.provider服務

複製代碼

app.provider('name',function(){
  ....
  return {
    ...
    $get:function(){
      ...
      return obj
    }
     }
})

複製代碼

name爲服務的名字,第二個參數接受一個函數,函數返回一個對象,返回的對象比如要有$get方法,$get方法必須要返回一個對象obj,這個對象就是真正被注入的服務.

栗子一:

js:

複製代碼

serviceApp.provider('myConfig',function(){
   return {
       $get:function(){
           var myname = 'code_bunny';
           var age = 12;
           var id = 1;
           return {
               name: myname,
               age: age,
               getId: function(){
                   return id
               }
           }
       }
   }
});

複製代碼

裝飾部分代碼同上.

運行結果: http://jsfiddle.net/2pz2ft73/

說明:

provider服務的第二個參數的返回值中必須要有$get方法(除了$get,還可以有其它方法,後面的例子會說到),$get方法就相當於factory服務的第二個參數,最後要返回一個對象,這個對象就是真正被注入的服務:

栗子二:

js:

複製代碼

serviceApp.provider('myConfig',function(){
    var id = 1;
    return {
        setID:function(newID){
            id = newID
        },
        $get:function(){
            var myname = 'code_bunny';
            var age = 12;
            return {
                name: myname,
                age: age,
                getId: function(){
                    return id
                }
            }
        }
    }
});
serviceApp.config(function(myConfigProvider){
    myConfigProvider.setID(2)
});

複製代碼

裝飾部分代碼同上.

運行結果:http://jsfiddle.net/hcpemex3/ 

說明:

這裏的provider服務不僅僅返回了$get方法,還返回了setID方法,然後id變量是寫在函數裏的,返回值的外面,形成一個閉包,可以被修改.

然後,在provider服務裏定義的方法,可以在config函數裏調用.注意調用的格式:

serviceApp.config(function(myConfigProvider){
    myConfigProvider.setID(2)
});

被注入的服務名不叫myConfig,而是myConfigProvider.然後在函數裏面可以調用myConfigProvider的setID方法(也就是myConfig的setID方法).

通過這種方式,使得我們的服務可以被手動配置,比如這裏可以配置id.

ng有很多內置的服務都有這樣的功能,比如$route服務,$location服務,當我們通過$routeProvider和$locationProvider來配置的時候,其本質就是這些服務是通過provider創建的.

注意點:

1.同constant注意點1

2.同constant注意點2

3.provider返回的服務也可以被裝飾,所以這裏myConfig服務擁有了money屬性.(裝飾具體怎麼用,下面會說);

 

六.裝飾服務

其實通過上面這麼多的例子,看也能看懂裝飾是什麼了...

app.config(function($provide){
    $provide.decorator('name',function($delegate){  
        $delegate.money = '100w';   
        return $delegate
    })
});

同樣是通過config,在參數函數中注入$provider服務,$provider服務有個decorator方法,它接受兩個參數,第一個參數'name',是要被裝飾的服務的名字,第二個參數是一個函數,函數中注入$delegate,$delegate就是被裝飾的服務的實例,然後在函數中操作$delegate,就相當於操作了該服務的實例.

注意:

1.最後一定要return $delegate,這樣服務纔算被裝飾完成了.

2.constant服務是不能被裝飾的.

栗子就不說了吧,上面的都是~ 

 

總結上面的內容:

1.服務的實例被注入到控制器以後,都是一個引用對象,無論被注入多少個控制器中,實際都指向同一個對象,所以,無論修改其中的哪一個,其它所有的服務都會被改變.

2.服務的實例被修改過後,ng不會自動同步,需要使用$scope.$watch()監測其變化並手動刷新視圖.

3.constant服務不能通過decorator進行裝飾.

4.一些固定的參數和方法,使用constant

5.可能被修改的參數和方法,使用value

6.通過邏輯處理後得到的參數或方法,使用factory

7.可以使用factory的也可以使用service,反之亦然(一般就是用factory)

8.可以手動配置參數的服務,使用provider

 

七.可以創建不同實例的服務

之前我們說到,所有的服務的實例都是引用對象,無論被注入多少個控制器中,實際都指向同一個對象,所以,無論修改其中的哪一個,其它所有的服務都會被改變.這就是ng服務的設計模式,一般不需要去改變,但如果有特殊需要,要能夠每次注入控制器後得到新的實例,可以這樣做:

我們給服務添加了一個方法,每次執行一次這個方法,都會創建一個新的實例,這樣,雖然在控制器裏注入的是服務實例還是同一個,但是在調用創建實例方法的時候,都會創建一個新的實例,然後就可以單獨修改這個實例,而不會影響到其它控制器:如下

js:

複製代碼

var serviceApp = angular.module('serviceApp',[]);
serviceApp.controller('myCtrl',function($scope,myConfig){
    var myConfigConstant = myConfig.create();
    $scope.name = myConfigConstant.name;
    $scope.age = myConfigConstant.age;
    angular.extend(myConfigConstant,{love:'zxg'});
    $scope.love = myConfigConstant.love;
    $scope.id = myConfigConstant.getId();
    $scope.$watch(myConfigConstant.name,function(){$scope.name = myConfigConstant.name;});
    myConfigConstant.name = 'white_bunny';
});
serviceApp.controller('myOtherCtrl',function($scope,myConfig){
    var myConfigConstant = myConfig.create();
    $scope.love = myConfigConstant.love;
    $scope.name = myConfigConstant.name;
    $scope.$watch(myConfigConstant.name,function(){$scope.name = myConfigConstant.name;});
});


/************************創建實例的服務************************/
serviceApp.factory('myConfig',function(){
    return {
       //服務返回的對象有一個create方法,該方法每次被執行都會返回一個新的constructorFun實例 
    create: constructorFun.createNew
    }
});

//創建一個構造函數
function constructorFun(){
    var myname = 'code_bunny';
    var age = 12;
    var id = 1;
    this.name = myname;
    this.age = age;
    this.id = id
}

//給構造函數添加createNew方法,用於實例化一個constructorFun.
constructorFun.createNew = function(){
    return new constructorFun()
};

//給構造函數添加原型的方法.使得它的實例可以繼承.
constructorFun.prototype = {
    getId: function(){
        return this.id
    }
};

複製代碼

運行效果: http://jsfiddle.net/fpoq4deo/1/

 

好了~五種服務類型就全部講完啦~~~ 

全部代碼託管: https://github.com/OOP-Code-Bunny/angular/tree/master/service

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章