forked from angular/code.angularjs.org
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathangular.service.html
164 lines (142 loc) · 6.2 KB
/
angular.service.html
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
<h1>angular.service</h1>
<fieldset class="workInProgress">
<legend>Work In Progress</legend>
This page is currently being revised. It might be incomplete or contain inaccuracies.
</fieldset>
<h2>Overview</h2>
<p>Services are substituable objects, which are wired together using dependency injection.
Each service could have dependencies (other services), which are passed in constructor.
Because JS is dynamicaly typed language, dependency injection can not use static types
to satisfy these dependencies, so each service must explicitely define its dependencies.
This is done by <code>$inject</code> property.</p>
<p>For now, life time of all services is the same as the life time of page.</p>
<h2>Built-in services</h2>
<p>The Angular framework provides a standard set of services for common operations.
You can write your own services and rewrite these standard services as well.
Like other core angular variables, the built-in services always start with $.</p>
<ul>
<li><code>angular.service.$browser</code></li>
<li><code>angular.service.$window</code></li>
<li><code>angular.service.$document</code></li>
<li><code>angular.service.$location</code></li>
<li><code>angular.service.$log</code></li>
<li><code>angular.service.$exceptionHandler</code></li>
<li><code>angular.service.$hover</code></li>
<li><code>angular.service.$invalidWidgets</code></li>
<li><code>angular.service.$route</code></li>
<li><code>angular.service.$xhr</code></li>
<li><code>angular.service.$xhr.error</code></li>
<li><code>angular.service.$xhr.bulk</code></li>
<li><code>angular.service.$xhr.cache</code></li>
<li><code>angular.service.$resource</code></li>
<li><code>angular.service.$cookies</code></li>
<li><code>angular.service.$cookieStore</code></li>
</ul>
<h2>Writing your own custom services</h2>
<p>Angular provides only set of basic services, so you will probably need to write your custom
service very soon. To do so, you need to write a factory function and register this function
to angular's dependency injector. This factory function must return an object - your service
(it is not called with new operator).</p>
<p><strong>angular.service</strong> has three parameters:</p>
<ul>
<li><code>{string} name</code> - Name of the service</li>
<li><code>{function()} factory</code> - Factory function (called just once by DI)</li>
<li><code>{Object} config</code> - Hash of configuration (<code>$inject</code>, <code>$creation</code>)</li>
</ul>
<p>If your service requires - depends on other services, you need to specify them
in config hash - property $inject. This property is an array of strings (service names).
These dependencies will be passed as parameters to the factory function by DI.
This approach is very useful when testing, as you can inject mocks/stubs/dummies.</p>
<p>Here is an example of very simple service. This service requires $window service (it's
passed as a parameter to factory function) and it's just a function.</p>
<p>This service simple stores all notifications and after third one, it displays all of them by
window alert.</p><div ng:non-bindable><pre class="brush: js; html-script: true;">
angular.service('notify', function(win) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join("\n"));
msgs = [];
}
};
}, {$inject: ['$window']});
</pre></div><p>And here is a unit test for this service. We use Jasmine spy (mock) instead of real browser's alert.</p><div ng:non-bindable><pre class="brush: js; html-script: true;">
var mock, notify;
beforeEach(function() {
mock = {alert: jasmine.createSpy()};
notify = angular.service('notify')(mock);
});
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"]);
});
</pre></div><h2>Injecting services into controllers</h2>
<p>Using services in a controllers is very similar to using service in other service.
Again, we will use dependency injection.</p>
<p>JavaScript is dynamic language, so DI is not able to figure out which services to inject by
static types (like in static typed languages). Therefore you must specify the service name
by the <code>$inject</code> property - it's an array that contains strings with names of services to be
injected. The name must match the id that service has been registered as with angular.
The order of the services in the array matters, because this order will be used when calling
the factory function with injected parameters. The names of parameters in factory function
don't matter, but by convention they match the service ids.</p><div ng:non-bindable><pre class="brush: js; html-script: true;">
function myController($loc, $log) {
this.firstMethod = function() {
// use $location service
$loc.setHash();
};
this.secondMethod = function() {
// use $log service
$log.info('...');
};
}
// which services to inject ?
myController.$inject = ['$location', '$log'];
</pre></div>
<h2>Example</h2>
<doc:example>
<doc:source>
<script type="text/javascript">
angular.service('notify', function(win) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join("\n"));
msgs = [];
}
};
}, {$inject: ['$window']});
function myController(notifyService) {
this.callNotify = function(msg) {
notifyService(msg);
};
}
myController.$inject = ['notify'];
</script>
<div ng:controller="myController">
<p>Let's try this simple notify service, injected into the controller...</p>
<input ng:init="message='test'" type="text" name="message" />
<button ng:click="callNotify(message);">NOTIFY</button>
</div>
</doc:source>
<doc:scenario></doc:scenario>
</doc:example>