mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
193 lines
6.4 KiB
Text
193 lines
6.4 KiB
Text
@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. `ngApp` triggers bootstrap sequence on given element, during which angular creates injector,
|
|
loads "phonecat" and "ng" modules and compiles the template.
|
|
3. The `ngController` 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}
|