此文是翻译文章,翻译自angularJS的service服务介绍。
angularJS的services是一些可以替代的对象,通过依赖注入的方式结合在一起。你可以在app应用中通过使用services服务组织和分享代码。达到功能的共享。
angularJS的services是:
@1:延迟实例化--只有在应用组件依赖一个服务时,angularJS才会实例化它。
@2:单例模式--每一个应用组件都是服务工厂创建的服务实例的一个引用。
angularJS提供了一些有用的服务(例如$http),但是在大部分app应用中,你仍然需要你自己的服务。
给依赖某个service的组件(controller,services,filter或者directive)添加一个服务作为组件的依赖。angularJS的依赖注入子系统将会处理这个依赖。
<div id="simple" ng-controller="MyController"> <p>创建一个notify服务,注入到controller中</p> <input ng-init="message='test'" ng-model="message" > <button ng-click="callNotify(message);">NOTIFY</button> <p>(you have to click 3 times to see an alert)</p> </div>
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 = []; } }; }]);
it('should test service', function() { expect(element(by.id('simple')) .element(by.model('message')) .getAttribute('value')) .toEqual('test'); });
通过在angularJS的module上注册service的名字和service factory函数,应用开发人员可以自由定义他们自己的服务。
"service factory function" 对于app应用使用的"service"创建一个单例模式的对象或者函数,注入到依赖特定服务的的任何组件(controller,service,filter或者directive)的服务返回这个对象或者函数。
服务通过module API注册到一个module。通常使用module factory API来注册一个服务:
var myModule = angular.module('myModule', []); myModule.factory('serviceId', function() { var shinyNewServiceInstance; // 构建服务结构 return shinyNewServiceInstance; });
注意这不是注册了一个服务实例,而是一个factory function工厂方法,只有在被调用的时候才会被实例化。
服务可以有它自己的依赖,就像在controller中声明依赖一样,通过在service的factory方法签名中声明依赖来指定依赖的服务。
更多的依赖介绍,请查看依赖注入文档。
下面的module例子有两个服务,各自有各自的依赖:
var batchModule = angular.module('batchModule', []); /** * `batchLog` service 可以对message按照顺序输出 * 每50s调用一次console.log. * * @param {*} message 将会被日志输出. */ 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` service. */ batchModule.factory('routeTemplateMonitor', ['$route', 'batchLog', '$rootScope', function($route, batchLog, $rootScope) { return { startMonitoring: function() { $rootScope.$on('$routeChangeSuccess', function() { batchLog($route.current ? $route.current.template : null); }); } }; }]);
在这个例子中,注意以下几点:
@1:“batchLog”依赖于内置服务“$interval”和“$log”。
@2:“routeTemplateMonitor”服务依赖于内置服务"$route"和我们创建的服务“batchLog”。
@3:两个服务都是通过数组注解的方式来声明依赖。
@4:数组注解的标识符的顺序和factory函数中的参数的顺序是一致的。
你也可以通过module的config服务中的"$provide"来注册一个服务。
angular.module('myModule', []).config(['$provide', function($provide) { $provide.factory('serviceId', function() { var shinyNewServiceInstance; // 定义服务的主体结构 return shinyNewServiceInstance; }); }]);
这种方式通常被用在单元测试中用来监测服务的依赖。
下面是一个根据上面创建angularJS服务例子实现的"notify"的单元测试。这个单元测试使用“Jasmine spy”来模拟真实浏览器中的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"]); });