服務
AngularJS服務是使用依賴注入(DI)鏈接在一塊兒的可替代對象。 可使用服務在整個應用程式中整理和分享程式碼。
AngularJS服務有:html
延遲初始化 - AngularJS只在應用程序組件依賴它時實例化服務。數組
單例 - 依賴於服務的每一個組件獲取對服務工廠生成的單個實例的引用。瀏覽器
AngularJS提供了幾個有用的服務(如$http),但對於大多數應用程序,你也想建立本身的。像其餘核心的AngularJS標識符同樣,內置服務老是以$開頭(例如$http)。
使用服務
要使用AngularJS服務,請將其添加爲依賴於依賴於服務的組件(控制器,服務,過濾器或指令)的依賴項。 AngularJS的依賴注入子系統負責其他的。app
index.htmlide
<div id="simple" ng-controller="MyController" ng-app="myServiceModule"> <p>讓咱們嘗試這個簡單的通知服務,注入到控制器</p> <input ng-init="message='test'" ng-model="message" > <button ng-click="callNotify(message);">NOTIFY</button> <p>(必須點擊3次才能看到提醒)</p> </div>
script.js函數
angular. module('myServiceModule', []). controller('MyController', ['$scope', 'notify', function($scope, notify) { $scope.callNotify = function(msg) { notify(msg); }; }]). factory('notify', ['$window', function(win) { var msgs = []; return function(msg) { msgs.push(msg); if (msgs.length === 3) { win.alert(msgs.join('\n')); msgs = []; } }; }]);
protractor.js單元測試
it('should test service', function() { expect(element(by.id('simple')).element(by.model('message')).getAttribute('value')) .toEqual('test'); });
須要點擊三次NOTIFY按鈕纔會出現彈出框內容
建立服務
應用程序開發人員可使用AngularJS模塊註冊服務的名稱和服務工廠函數來自由定義本身的服務。
服務工廠函數生成表示對應用程序其他部分的服務的單個對象或函數。 服務返回的對象或函數被注入到指定對服務的依賴性的任何組件(控制器,服務,過濾器或指令)中。
註冊服務
服務經過Module API註冊到模塊。 一般您使用Module factory API註冊服務:測試
var myModule = angular.module('myModule', []); myModule.factory('serviceId', function() { var shinyNewServiceInstance; // 構造shinyNewServiceInstance的工廠函數體 return shinyNewServiceInstance; });
此時不是註冊服務實例,而是一個在調用時將建立此實例的工廠函數。
依賴
服務能夠有本身的依賴。 就像在控制器中聲明依賴項同樣,能夠經過在服務的工廠函數簽名中指定依賴性來聲明它們。
下面的示例模塊有兩個服務,每一個具備各類依賴關係:code
var batchModule = angular.module('batchModule', []); /** *`batchLog'服務容許消息在內存中排隊,而且每50秒刷新到console.log */ batchModule.factory('batchLog', ['$interval', '$log', function($interval, $log) { var messageQueue = []; function log() { if (messageQueue.length) { $log.log('batchLog messages: ', messageQueue); messageQueue = []; } } // 開始按期檢查 $interval(log, 50000); return function(message) { messageQueue.push(message); } }]); /** *`routeTemplateMonitor`監視每一個`$ route`更改,並經過`batchLog`服務記錄當前模板 */ batchModule.factory('routeTemplateMonitor', ['$route', 'batchLog', '$rootScope', function($route, batchLog, $rootScope) { return { startMonitoring: function() { $rootScope.$on('$routeChangeSuccess', function() { batchLog($route.current ? $route.current.template : null); }); } }; }]);
在示例中,須要注意的是:htm
batchLog服務取決於內置的$interval和$log服務。
routeTemplateMonitor服務取決於內置的$route服務和$rootscope和咱們的自定義batchLog服務。
兩個服務都使用數組符號來聲明它們的依賴關係。
數組中標識符的順序與工廠函數中參數名稱的順序相同。
還能夠經過模塊的配置函數中的$provide服務註冊服務:
angular.module('myModule', []).config(['$provide', function($provide) { $provide.factory('serviceId', function() { var shinyNewServiceInstance; // 構造shinyNewServiceInstance的工廠函數體 return shinyNewServiceInstance; }); }]);
這種技術一般用於單元測試中來模擬服務的依賴。
單元測試
如下是來自上面的建立服務示例的通知服務的單元測試。 單元測試示例使用Jasmine spy(mock),而不是真正的瀏覽器alert。
var mock, notify; beforeEach(module('myServiceModule')); beforeEach(function() { mock = {alert: jasmine.createSpy()}; module(function($provide) { $provide.value('$window', mock); }); inject(function($injector) { notify = $injector.get('notify'); }); }); it('should not alert first two notifications', function() { notify('one'); notify('two'); expect(mock.alert).not.toHaveBeenCalled(); }); it('should alert all after third notification', function() { notify('one'); notify('two'); notify('three'); expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree"); }); it('should clear messages after alert', function() { notify('one'); notify('two'); notify('third'); notify('more'); notify('two'); notify('third'); expect(mock.alert.calls.count()).toEqual(2); expect(mock.alert.calls.mostRecent().args). toEqual(["more\ntwo\nthird"]); });