原文地址:http://angular-tips.com/blog/2013/08/understanding-service-types/
Angular中有幾種不同類型的services。每一種都有自己的獨特用法。
需要記住的非常重要的一點是service總是一個單體,無論是哪種類型的service,這是個很被期望的行爲。
註釋:單體是一種設計模式,它限制了每一個類僅能夠實例化爲一個對象。無論我們在什麼地方注入我們的service,將永遠使用同一個實例。
Provider
Provider是幾乎所有service的根源(除了contant),但也是最複雜並且可以配置。我們先看下一個基本用法:
app.provider('foo', function() { return { $get: function() { var thisIsPrivate = "Private"; function getPrivate() { return thisIsPrivate; } return { variable: "This is public", getPrivate: getPrivate }; } };});
一個provider,最簡單的形式是返回一個$get方法,p rovider註冊的即是我們在其他組件中可以注入的部分。如果我們有一個controller,想把foo作爲一個provider注入,我們注入的部分就是$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方法的外部,爲的是在config中可以配置。爲神馬我們需要這麼做呢?將setter方法增加到$get是不是更加簡單?這是因爲不同的目的。
想象一下你想創建一個通用庫用來管理models和REST請求。如果你將目標URLs硬編碼,這樣的庫不再通用,所以比較好的想法是將URLs可以配置,我們可以創建一個provider使得URLs在config方法中可以配置。
需要注意的是我們將nameProvider注入到config而不是name,如果是注入到$get中可以使用name(後半句是個人理解,不知道是否合理,留作一個問題,後續驗證下)。
看到這裏,我們意識到,我們已經在應用中配置過一些服務,像$routeProvider和$locationProvider配置routes和html5mode。
Providers有兩種注入方式,provider constructor 和$get方法。provider constructor僅能注入其他providers和constants(在配置方面有相同的限制)。在$get方法中可以注入除了其他的provider的服務(但是可以注入其他provider的$get方法)
注意:注入一個provider使用:name+'Provider', 如果注入到它的$get方法可以使用name
Factory
Provider非常棒,但是相當靈活也比較複雜,我們只是想使用它的$get方法?我的意思是,根本不用配置,這樣的話我們嗨可以使用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; });
正如你看到的,把provider的$get方法移到了一個factory中,和第一的provider相比簡單多了。事實上,factory內部只用了一個$get方法。
正如前面介紹的,所有的service都是單例的,如果我們在一個地方修改foo.variable的值,其他地方的值也將會修改。
我們可以注入所有的服務(除了providers)到factory。可以注入(如上面的foo,bar)到任何的地方除了provider constructor 和 config方法。(具體的例子可以參考原文)
Value
Factory很好用,但是如果我們僅存儲一個簡單的值呢?我的意思是,沒有注入,只是一個簡單的值或對象,angular提供一個叫做value的service。
app.value('foo', 'A simple value');
value的內部實現只是一個fa ctory。jiran它是一個factory那麼仍然要遵循它的注入方法。AKA不能被注入到provider constructor 和config方法
Service
到現在我們提到了複雜的provider,比較簡單多factory和value 服務,service服務時什麼樣子的?我們先看下這個例子:
app.service('foo', function() { var thisIsPrivate = "Private"; this.variable = "This is public"; this.getPrivate = function() { return thisIsPrivate; }; });
service服務的工作方式可以factory很相似,區別也是很簡單:factory接受一個方法,這個方法在創建時調用,service接受一個構造方法(內部使用Object.create創建而不是使用new),
事實上,下面的代碼將實現相同的功能:
app.factory('foo2', function() { return new Foobar(); }); function Foobar() { var thisIsPrivate = "Private"; this.variable = "This is public"; this.getPrivate = function() { return thisIsPrivate; }; }
Foobar是一個構造方法,在angular處理過程中被實例化然後返回。Foobar會被初始化一次,接下來我們使用factory會返回同樣的實例。
有了構造方法,可以使用service服務了,如下:
app.service('foo3', Foobar);
Constant
constant是provider的子類型,和value 服務的用法比較相似:
app.constant('fooConfig', { config1: true, config2: "Default config2" });
和value有什麼區別呢?constant可以被注入到任何地方,包括provider constructor 和config 方法。這就是爲什麼使用constant 服務爲directive設置默認配置,因爲可以在config方法中修改這些配置。
你可能正在考慮爲什麼命名爲constant,這只是一個設計決定,並不知道它背後的原因。
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中最酷的特性之一。我們可以使用很多方法來創造它們,我們僅僅需要找到符合我們需求的方法然後實現它。