服务
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"]); });