AngularJS权威指南-服务

《AngularJS 权威教程》读书笔记,第十四章——服务。

简介

处于内存占用和性能的考虑,控制器只会在需要时被实例化,并且不再需要就会被销毁。这也就是说:每次切换路由或重新加载视图时,当前的控制器会被 AngularJS 清除掉。
服务提供了一种能在应用的整个声明周期内保持数据的方法,它能够在控制器之间进行通信,并且能保证数据的一致性。
服务是一个单例对象,在每个应用中只会被实例化一次(被 $injector 实例化),并且是延迟加载的(需要时才会被创建,懒汉式单例)。
在自定义服务之前注入所有的 AngularJS 内置服务,这是约定俗成的规则。

注册一个服务

使用 angular.module 的 factory API 创建服务,是最常见也是最灵活的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
angular.module('myApp.services', [])
.config(function($sceDelegateProvider){
$sceDelegateProvider.resourceUrlWhitelist([
// Allow same origin resource loads.
'self',
'https://api.github.com/**'
]);
$sceDelegateProvider.resourceUrlBlacklist([
'https://www.baidu.com'
]);
})
.factory('githubService', function($http){
var githubUrl = 'https://api.github.com';
var runUserRequest = function(username, path){
return $http({
method: 'JSONP',
url: githubUrl + '/users/' + username + '/' + path + ''
});
};
return {
events: function(username){
return runUserRequest(username, 'events');
}
};
});

使用服务

可以在控制器、指令、过滤器或另外一个服务中通过依赖声明的方式来使用服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>Parse Expression Example</title>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="js/angular.min.js"></script>
<style type="text/css">
</style>
</head>
<body ng-controller="ServiceController">
<div>
<label for="username">Type in a Github username</label>
<input type="text" name="username" ng-model="username" placeholder="Enter a Github username">
<ul>
<li ng-repeat="event in events">
{{ event.actor.login }} | {{ event.repo.name }}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
angular.module('myApp.services', [])
.config(function($sceDelegateProvider){
$sceDelegateProvider.resourceUrlWhitelist([
// Allow same origin resource loads.
'self',
'https://api.github.com/**'
]);
$sceDelegateProvider.resourceUrlBlacklist([
'https://www.baidu.com'
]);
})
.factory('githubService', function($http){
var githubUrl = 'https://api.github.com';
var runUserRequest = function(username, path){
return $http({
method: 'JSONP',
url: githubUrl + '/users/' + username + '/' + path + ''
});
};
return {
events: function(username){
return runUserRequest(username, 'events');
}
};
});
angular.module('myApp', ['myApp.services'])
.controller('ServiceController', function($scope, $timeout, githubService) {
var timeout;
$scope.$watch('username', function(newUsername){
if(newUsername){
if(timeout){
$timeout.cancel(timeout);
}
timeout = $timeout(function(){
githubService.events(newUsername)
.then(function(res){
if(res.data && angular.isArray(res.data.data)){
$scope.events = res.data.data;
}else {
$scope.events = [];
}
});
}, 900);
}
});
});
</script>
</html>

服务的作用不光是将类似功能打包在一起,也可以在控制器之间共享数据。

其他创建服务的方式

AngularJS 中共有五种方法创建服务:

  1. factory:用法如上
  2. service:可以注册一个支持构造函数的服务,它允许我们为服务对象注册一个构造函数
  3. provider:所有服务工厂都是由 $provide 服务创建的,$provide 服务负责在运行时初始化这些提供者,并在 $providerCache 中注册服务。如果希望在 config 函数中对服务进行配置,必须用 provider 来定义服务
  4. constant:创建常量服务,常量服务通常注入到配置函数中,常量服务不能被装饰器拦截
  5. value:创建值服务,值服务不能注入到配置函数中,但可以注入到其他内容中

装饰器

装饰器可以在服务实例化时对其进行拦截,可以对服务进行扩展,或者用另外的内容完全代替它。
装饰器是非常强大的,它不仅可以应用在自己的服务上,也可以对 AngularJS 的核心服务进行拦截、中断甚至替换功能等操作。