AngularJS–service(服务)

时间:2023-12-30 21:01:08

点击查看AngularJS系列目录
转载请注明出处:http://www.cnblogs.com/leosx/


服务

Angular的服务也是使用依赖注入(dependency injection (DI))去获取相关对象的实例的。你可以在整个app当中,去共享你的代码。

Angular的服务有两点特性:

1、懒惰实例化 -- 只有当你依赖了它的时候,它才会被创建。

2、单例模式 -- 每一个依赖了它的组件只会创建一个实例。服务的创建是由服务工厂来创建的。

Angular官方提供了一些常用的服务(例如:$http),但是但部分app中,我们都会需要去创建属于自己的服务。

注意: Angular自己提供的服务或者核心对象,都是以$ 开头的(例如 $http) 也就是说在自定义服务的时候,最好不要以$开头。以免覆盖影响到了Angular自带的服务或功能。

使用服务

要使用Angular的服务,就得将它注入给需要它的组件(包括:指令directive、控制器controller、服务service、过滤器filter等)。具体如何操作,见下面例子:

第一个文件:index.html

<div id="simple" ng-controller="MyController">
<p>Let's try this simple notify service, injected into the 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>

第二个文件: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');
});

示例如下:

AngularJS–service(服务)

自定义服务

开发人员可以通过注册服务的名字实现服务的工厂方法(factory)来自定义服务。

服务会被服务工厂(factory)所创建,并且是对于一个调用组件来说,是单例的。然后再把这个实例注入到依赖这个服务的组件的构造函数中去。

注册服务

服务是使用Module factory(模块的factory方法) API去注册的。如下:

var myModule = angular.module('myModule', []);
myModule.factory('serviceId', function() {
var shinyNewServiceInstance;
// factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});

服务的依赖

服务也可以有自己的依赖。就像在一个控制器中声明依赖一样,我们同样可以通过在服务的工厂函数参数中指定它们所依赖其它组件。

关于更多和依赖相关的文档,点击这里(dependency injection)可以查看。

下面的例子中的module有着两个服务,每个服务都有着依赖组件的组件。

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

/**
* The `batchLog` service allows for messages to be queued in memory and flushed
* to the console.log every 50 seconds.
*
* @param {*} message Message to be logged.
*/
batchModule.factory('batchLog', ['$interval', '$log', function($interval, $log) {
var messageQueue = []; function log() {
if (messageQueue.length) {
$log.log('batchLog messages: ', messageQueue);
messageQueue = [];
}
} // start periodic checking
$interval(log, 50000); return function(message) {
messageQueue.push(message);
}
}]); /**
* `routeTemplateMonitor` monitors each `$route` change and logs the current
* template via the `batchLog` service.
*/
batchModule.factory('routeTemplateMonitor', ['$route', 'batchLog', '$rootScope',
function($route, batchLog, $rootScope) {
$rootScope.$on('$routeChangeSuccess', function() {
batchLog($route.current ? $route.current.template : null);
});
}]);

在上面的例子中:

1、batchLog 服务依赖了Angular内置的$interval 服务和 $log 服务。

2、routeTemplateMonitor 服务依赖了Angular内置的 $route 服务和我们自定义的batchLog 服务。

3、这两种服务都使用数组表示法来声明它们的依赖。

4、注意,依赖声明的顺序就是构造函数中参数的顺序。

使用 $provide注册服务

你也可以使用模块的配置功能$provide去注册服务(自定义服务),如下:

angular.module('myModule', []).config(['$provide', function($provide) {
$provide.factory('serviceId', function() {
var shinyNewServiceInstance;
// factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
}]);

这种技术通常用于在单元测试中模拟出一个服务的依赖。

单元测试

下面是一个用于测试上面的例子创建的notify服务的单元测试。单元测试示例发出的告警是使用Jasmine spy 模拟出来的,而不是真正的浏览器发出的告警信息。

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.callCount).toEqual(2);
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
});