mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-08 22:37:20 +08:00
295 lines
10 KiB
Plaintext
295 lines
10 KiB
Plaintext
@ngdoc overview
|
|
@name Dependency Injection
|
|
@description
|
|
|
|
# Dependency Injection
|
|
|
|
Dependency Injection (DI) is a software design pattern that deals with how components get hold of
|
|
their dependencies.
|
|
|
|
The Angular injector subsystem is in charge of creating components, resolving their dependencies,
|
|
and providing them to other components as requested.
|
|
|
|
For in-depth discussion about DI, see
|
|
[Dependency Injection](http://en.wikipedia.org/wiki/Dependency_injection) at Wikipedia,
|
|
[Inversion of Control](http://martinfowler.com/articles/injection.html) by Martin Fowler,
|
|
or read about DI in your favorite software design pattern book.
|
|
|
|
## DI in a Nutshell
|
|
|
|
There are only three ways a component (object or function) can get a hold of its dependencies:
|
|
|
|
1. The component can create the dependency, typically using the `new` operator.
|
|
2. The component can look up the dependency, by referring to a global variable.
|
|
3. The component can have the dependency passed to it where it is needed.
|
|
|
|
The first two options of creating or looking up dependencies are not optimal because they hard
|
|
code the dependency to the component. This makes it difficult, if not impossible, to modify the
|
|
dependencies. This is especially problematic in tests, where it is often desirable to provide mock
|
|
dependencies for test isolation.
|
|
|
|
The third option is the most viable, since it removes the responsibility of locating the
|
|
dependency from the component. The dependency is simply handed to the component.
|
|
|
|
```js
|
|
function SomeClass(greeter) {
|
|
this.greeter = greeter;
|
|
}
|
|
|
|
SomeClass.prototype.doSomething = function(name) {
|
|
this.greeter.greet(name);
|
|
}
|
|
```
|
|
|
|
In the above example `SomeClass` is not concerned with creating or locating the `greeter`
|
|
dependency, it is simply handed the `greeter` when it is instantiated.
|
|
|
|
This is desirable, but it puts the responsibility of getting hold of the dependency on the
|
|
code that constructs `SomeClass`.
|
|
|
|
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-module-injector.png">
|
|
|
|
To manage the responsibility of dependency creation, each Angular application has an {@link
|
|
angular.injector injector}. The injector is a
|
|
[service locator](http://en.wikipedia.org/wiki/Service_locator_pattern) that is responsible for
|
|
construction and lookup of dependencies.
|
|
|
|
Here is an example of using the injector service:
|
|
|
|
```js
|
|
// Provide the wiring information in a module
|
|
var myModule = angular.module('myModule', []);
|
|
```
|
|
|
|
Teach the injector how to build a `greeter` service. Notice that `greeter` is dependent on the
|
|
`$window` service. The `greeter` service is an object that contains a `greet` method.
|
|
|
|
```js
|
|
myModule.factory('greeter', function($window) {
|
|
return {
|
|
greet: function(text) {
|
|
$window.alert(text);
|
|
}
|
|
};
|
|
});
|
|
```
|
|
|
|
Create a new injector that can provide components defined in our `myModule` module and request our
|
|
`greeter` service from the injector. (This is usually done automatically by angular bootstrap).
|
|
|
|
```js
|
|
var injector = angular.injector(['myModule', 'ng']);
|
|
var greeter = injector.get('greeter');
|
|
```
|
|
|
|
Asking for dependencies solves the issue of hard coding, but it also means that the injector needs
|
|
to be passed throughout the application. Passing the injector breaks the
|
|
[Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter). To remedy this, we use a declarative
|
|
notation in our HTML templates, to hand the responsibility of creating components over to the
|
|
injector, as in this example:
|
|
|
|
```html
|
|
<div ng-controller="MyController">
|
|
<button ng-click="sayHello()">Hello</button>
|
|
</div>
|
|
```
|
|
|
|
```js
|
|
function MyController($scope, greeter) {
|
|
$scope.sayHello = function() {
|
|
greeter.greet('Hello World');
|
|
};
|
|
}
|
|
```
|
|
|
|
When Angular compiles the HTML, it processes the `ng-controller` directive, which in turn
|
|
asks the injector to create an instance of the controller and its dependencies.
|
|
|
|
```js
|
|
injector.instantiate(MyController);
|
|
```
|
|
|
|
This is all done behinds the scenes. Notice that by having the `ng-controller` ask the injector to
|
|
instantiate the class, it can satisfy all of the dependencies of `MyController` without the
|
|
controller ever knowing about the injector.
|
|
|
|
This is the best outcome. The application code simply declares the dependencies it needs, without
|
|
having to deal with the injector. This setup does not break the Law of Demeter.
|
|
|
|
|
|
## Dependency Annotation
|
|
|
|
**How does the injector know what components need to be injected?**
|
|
|
|
The application developer needs to provide annotation information that the injector uses in order
|
|
to resolve the dependencies. Throughout Angular, certain API functions are invoked using the
|
|
injector, as per the API documentation. The injector needs to know what services to inject into
|
|
the function. There are three equivalent ways of annotating your code with service name
|
|
information:
|
|
|
|
- Implicitly from the function parameter names
|
|
- Using the `$inject` property annotation
|
|
- Using the inline array annotation
|
|
|
|
These can be used interchangeably as you see fit and are equivalent.
|
|
|
|
### Implicit Dependencies
|
|
|
|
The simplest way to get hold of the dependencies, is to assume that the function parameter names
|
|
are the names of the dependencies.
|
|
|
|
```js
|
|
function MyController($scope, greeter) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
Given a function the injector can infer the names of the service to inject by examining the
|
|
function declaration and extracting the parameter names. In the above example `$scope`, and
|
|
`greeter` are two services which need to be injected into the function.
|
|
|
|
While straightforward, this method will not work with JavaScript minifiers/obfuscators as they
|
|
rename the method parameter names. This makes this way of annotating only useful for
|
|
[pretotyping](http://www.pretotyping.org/), and demo applications.
|
|
|
|
### `$inject` Property Annotation
|
|
|
|
To allow the minifiers to rename the function parameters and still be able to inject right services,
|
|
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
|
|
of service names to inject.
|
|
|
|
```js
|
|
var MyController = function(renamed$scope, renamedGreeter) {
|
|
...
|
|
}
|
|
MyController['$inject'] = ['$scope', 'greeter'];
|
|
```
|
|
|
|
In this scenario the ordering of the values in the `$inject` array must match the ordering of the
|
|
arguments to inject. Using above code snippet as an example, `$scope` will be injected into
|
|
`renamed$scope` and `greeter` into `renamedGreeter`. Care must be taken that the `$inject`
|
|
annotation is kept in sync with the actual arguments in the function declaration.
|
|
|
|
This method of annotation is useful for controller declarations since it assigns the annotation
|
|
information with the function.
|
|
|
|
### Inline Array Annotation
|
|
|
|
Sometimes using the `$inject` annotation style is not convenient such as when annotating
|
|
directives or services defined inline by a factory function.
|
|
|
|
For example:
|
|
|
|
```js
|
|
someModule.factory('greeter', function($window) {
|
|
// ...
|
|
});
|
|
```
|
|
|
|
Results in code bloat due to needing a temporary variable:
|
|
|
|
```js
|
|
var greeterFactory = function(renamed$window) {
|
|
// ...
|
|
};
|
|
|
|
greeterFactory.$inject = ['$window'];
|
|
|
|
someModule.factory('greeter', greeterFactory);
|
|
```
|
|
|
|
For this reason the third annotation style is provided as well.
|
|
|
|
```js
|
|
someModule.factory('greeter', ['$window', function(renamed$window) {
|
|
// ...
|
|
}]);
|
|
```
|
|
|
|
Here, instead of simply providing the factory function, we pass an array, whose elements consist of
|
|
a list of strings (the names of the dependencies) followed by the function itself.
|
|
|
|
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
|
|
where injection is supported.
|
|
|
|
## Where Can I Use DI?
|
|
|
|
DI is pervasive throughout Angular. You can use it when defining components or when providing `run`
|
|
and `config` blocks for a module.
|
|
|
|
- Components such as services, directives, filters and animations are defined by an injectable factory
|
|
method or constructor function. These components can be injected with "service" components as
|
|
dependencies.
|
|
|
|
- The `run` and `config` methods accept a function, which can also be injected with "service"
|
|
components as dependencies.
|
|
|
|
- Controllers are defined by a constructor function, which can be injected with any of the "service"
|
|
components as dependencies, but they can also be provided with special dependencies. See "DI in
|
|
Controllers" below.
|
|
|
|
|
|
### Factory Methods
|
|
|
|
Factory methods are responsible for creating most objects in Angular. Examples are directives,
|
|
services, and filters. The factory methods are registered with the module, and the recommended way
|
|
of declaring factories is:
|
|
|
|
```js
|
|
angular.module('myModule', [])
|
|
.factory('serviceId', ['depService', function(depService) {
|
|
...
|
|
}])
|
|
.directive('directiveName', ['depService', function(depService) {
|
|
...
|
|
}])
|
|
.filter('filterName', ['depService', function(depService) {
|
|
...
|
|
}]);
|
|
```
|
|
|
|
### Module Methods
|
|
|
|
We can specify functions to run at configuration and run time for a module by calling the `run` and
|
|
`config` methods. These functions are injectable with dependencies just like the factory functions
|
|
above.
|
|
|
|
```js
|
|
angular.module('myModule', [])
|
|
.config(['depProvider', function(depProvider){
|
|
...
|
|
}])
|
|
.run(['depService', function(depService) {
|
|
...
|
|
}]);
|
|
```
|
|
|
|
### Controllers
|
|
|
|
Controllers are "classes" or "constructor functions" that are responsible for providing the
|
|
application behavior that supports the declarative markup in the template. The recommended way of
|
|
declaring Controllers is using the array notation:
|
|
|
|
```js
|
|
someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
|
|
...
|
|
$scope.aMethod = function() {
|
|
...
|
|
}
|
|
...
|
|
}]);
|
|
```
|
|
|
|
This avoids the creation of global functions for controllers and also protects against minification.
|
|
|
|
Controllers are special in that, unlike services, there can be many instances of them in the
|
|
application. For example, there would be one instance for every `ng-controller` directive in the template.
|
|
|
|
Moreover, additional dependencies are made available to Controllers:
|
|
|
|
* {@link scope `$scope`}: Controllers are always associated with a point in the DOM and so are provided with
|
|
access to the {@link scope scope} at that point. Other components, such as services only have access to the
|
|
singleton {@link $rootScope} service.
|
|
* {@link $route} resolves: If a controller is instantiated as part of a route, then any values that
|
|
are resolved as part of the route are made available for injection into the controller.
|