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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
@ngdoc overview
@name Developer Guide: DI: Understanding DI in Angular
@description
While DI is widely used in statically typed languages such as Java or C++, it has not been widely
used in JavaScript. Angular brings the benefits of DI into JavaScript apps.
In angular, DI is implemented as a subsystem that manages dependencies between services,
controllers, widgets, and filters.
Services are objects that handle common tasks in web applications. Angular provides several {@link
api/angular.module.ng built-in services}, and you can create your
{@link dev_guide.services.creating_services own custom services}.
The main job of angular's DI subsystem is to provide services to angular components that depend on
them. The way the DI subsystem provides services is as follows: all services are registered with
angular's {@link api/angular.module.ng service API}, and all components that depend on services
define those dependencies as a property (`$inject`). With this information, the DI subsystem
manages the creation of service objects and the provision of those objects to the components that
need them, at the time they need them. The following illustration steps through the sequence of
events:
<img src="img/guide/di_sequence_final.png">
In the illustration above, the dependency injection sequence proceeds as follows:
1. Module "phonecat" is created and all the service providers are registered with this module.
(the "ng" module is created by Angular behind the scenes as well)
2. `ng-app` triggers bootstrap sequence on given element, during which angular creates injector,
loads "phonecat" and "ng" modules and compiles the template.
3. The `ng-controller` directive implicitly creates a new child scope and instantiates
`PhoneListCtrl` controller.
4. Injector identifies the `$http` service as `PhoneListCtrl` controller's only dependency.
5. Injector checks its instances cache whether the `$http` service has already been instantiated.
If not uses the provider from the available modules to construct it.
6. Injector provides the instance of `$http` service to the `PhoneListCtrl` controller constructor.
## How Scope Relates to DI
The root scope of the application is just a service that is available for injection to any part of
the application under the service name "$rootScope".
## Inferring dependencies from the signature of the factory function or constructor
**EXPERIMENTAL FEATURE**: This is an experimental feature. See the important note at the end of
this section for drawbacks.
We resort to `$inject` and our own annotation because there is no way in JavaScript to get a list
of arguments. Or is there? It turns out that calling `.toString()` on a function returns the
function declaration along with the argument names as shown below:
<pre>
function myFn(a,b){}
expect(myFn.toString()).toEqual('function myFn(a,b){}');
</pre>
This means that angular can infer the function names after all and use that information to generate
the `$inject` annotation automatically. Therefore the following two function definitions are
equivalent:
<pre>
// given a user defined service
angular.module('module1', [], function($provide) {
$provide.factory('serviceA', ...);
});
// inject '$window', 'serviceA', curry 'name';
function fnA($window, serviceA, name){};
fnA.$inject = ['$window', 'serviceA'];
// inject '$window', 'serviceA', curry 'name';
function fnB($window, serviceA_, name){};
// implies: fnB.$inject = ['$window', 'serviceA'];
</pre>
If angular does not find a `$inject` annotation on the function, then it calls the `.toString()`
method and tries to infer what should be injected by using function argument names as dependency
identifiers.
**IMPORTANT**
Minifiers/obfuscators change the names of function arguments and will therefore break the `$inject`
inference. For this reason, either explicitly declare the `$inject` or do not use
minifiers/obfuscators. In the future, we may provide a pre-processor which will scan the source
code and insert the `$inject` into the source code so that it can be minified/obfuscated.
### Dependency inference and variable name shadowing
During inference, the injector considers argument names with leading and trailing underscores to be
equivivalent to the name without these underscores. For example `_fooSvc_` argument name is treated
as if it was `fooSvc`, this is useful especially in tests where variable name shadowing can cause
some friction. This is best illustrated on examples:
When testing a service, it's common to need a reference to it in every single test. This can be
done in jasmine with DI inference like this:
<pre>
describe('fooSvc', function() {
it('should do this thing', inject(function(fooSvc) {
//test fooSvc
}));
it('should do that thing', inject(function(fooSvc) {
//test fooSvc
}));
// more its
});
</pre>
... but having to inject the service over and over gets easily tiresome.
It's likely better to rewrite these tests with a use of jasmine's `beforeEach`:
<pre>
describe('fooSvc', function() {
var fooSvc;
beforeEach(inject(function(fooSvc) {
fooSvc = fooSvc; // DOESN'T WORK! outer fooSvc is being shadowed
}));
it('should do this thing', function() {
//test fooSvc
});
it('should do that thing', function() {
//test fooSvc
});
// more its
});
</pre>
This obviously won't work because `fooSvc` variable in the describe block is being shadowed by the
`fooSvc` argument of the beforeEach function. So we have to resort to alternative solutions, like
for example use of array notation to annotate the beforeEach fn:
<pre>
describe('fooSvc', function() {
var fooSvc;
beforeEach(inject(['fooSvc', function(fooSvc_) {
fooSvc = fooSvc_;
}]));
it('should do this thing', function() {
//test fooSvc
});
it('should do that thing', function() {
//test fooSvc
});
});
</pre>
That's better, but it's still annoying, especially if you have many services to inject.
To resolve this shadowing problem, the injector considers `_fooSvc_` argument names equal to
`fooSvc`, so the test can be rewritten like this:
<pre>
describe('fooSvc', function() {
var fooSvc;
beforeEach(inject(function(_fooSvc_) {
fooSvc = _fooSvc_;
}));
it('should do this thing', function() {
//test fooSvc
});
it('should do that thing', function() {
//test fooSvc
});
// more its
});
</pre>
## Related Topics
* {@link dev_guide.services Angular Services}
## Related API
* {@link api/angular.module.ng Services API}
|