mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-04 22:49:48 +08:00
docs(guide/di): clarify what can be injected into what
Also do some general housekeeping and tidying of the page. Closes #6999
This commit is contained in:
@@ -4,30 +4,29 @@
|
||||
|
||||
# Dependency Injection
|
||||
|
||||
Dependency Injection (DI) is a software design pattern that deals with how code gets hold of its
|
||||
dependencies.
|
||||
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 service instantiation, resolution
|
||||
of dependencies, and provision of dependencies to components as requested.
|
||||
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
|
||||
## DI in a Nutshell
|
||||
|
||||
There are only three ways an object or a function can get a hold of its dependencies:
|
||||
|
||||
1. The dependency can be created, typically using the `new` operator.
|
||||
2. The dependency can be looked up by referring to a global variable.
|
||||
3. The dependency can be passed in to where it is needed.
|
||||
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. 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.
|
||||
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.
|
||||
@@ -42,8 +41,8 @@ SomeClass.prototype.doSomething = function(name) {
|
||||
}
|
||||
```
|
||||
|
||||
In the above example `SomeClass` is not concerned with locating the `greeter` dependency, it
|
||||
is simply handed the `greeter` at runtime.
|
||||
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`.
|
||||
@@ -51,74 +50,90 @@ 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 that is responsible for
|
||||
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
|
||||
angular.module('myModule', []).
|
||||
var myModule = angular.module('myModule', []);
|
||||
```
|
||||
|
||||
// Teach the injector how to build a 'greeter'
|
||||
// Notice that greeter itself is dependent on '$window'
|
||||
factory('greeter', function($window) {
|
||||
// This is a factory function, and is responsible for
|
||||
// creating the 'greet' service.
|
||||
return {
|
||||
greet: function(text) {
|
||||
$window.alert(text);
|
||||
}
|
||||
};
|
||||
});
|
||||
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.
|
||||
|
||||
// New injector is created from the module.
|
||||
// (This is usually done automatically by angular bootstrap)
|
||||
```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']);
|
||||
|
||||
// Request any dependency from the injector
|
||||
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 turn the
|
||||
dependency lookup responsibility to the injector by declaring the dependencies as in this example:
|
||||
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
|
||||
<!-- Given this HTML -->
|
||||
<div ng-controller="MyController">
|
||||
<button ng-click="sayHello()">Hello</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```js
|
||||
// And this controller definition
|
||||
function MyController($scope, greeter) {
|
||||
$scope.sayHello = function() {
|
||||
greeter.greet('Hello World');
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
// The 'ng-controller' directive does this behind the scenes
|
||||
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);
|
||||
```
|
||||
|
||||
Notice that by having the `ng-controller` 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 asks for the dependencies it needs, without having to
|
||||
deal with the injector. This setup does not break the Law of Demeter.
|
||||
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 service needs to be injected?
|
||||
**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. Below are three equivalent ways of annotating your code with service name
|
||||
information. These can be used interchangeably as you see fit and are equivalent.
|
||||
the function. There are three equivalent ways of annotating your code with service name
|
||||
information:
|
||||
|
||||
### Inferring Dependencies
|
||||
- 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.
|
||||
@@ -134,11 +149,12 @@ function declaration and extracting the parameter names. In the above example `$
|
||||
`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.
|
||||
rename the method parameter names. This makes this way of annotating only useful for
|
||||
[pretotyping](http://www.pretotyping.org/), and demo applications.
|
||||
|
||||
### `$inject` Annotation
|
||||
### `$inject` Property Annotation
|
||||
|
||||
To allow the minifers to rename the function parameters and still be able to inject right services
|
||||
To allow the minifers 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.
|
||||
|
||||
@@ -149,18 +165,18 @@ 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.
|
||||
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 Annotation
|
||||
### Inline Array Annotation
|
||||
|
||||
Sometimes using the `$inject` annotation style is not convenient such as when annotating
|
||||
directives.
|
||||
directives or services defined inline by a factory function.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -190,18 +206,69 @@ 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 depenendencies) 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?
|
||||
## Where Can I Use DI?
|
||||
|
||||
DI is pervasive throughout Angular. You can use it in controllers, services, directives, filters,
|
||||
animations, and `run` and `config` blocks.
|
||||
DI is pervasive throughout Angular. You can use it when defining components or when providing `run`
|
||||
and `config` blocks for a module.
|
||||
|
||||
### DI in controllers
|
||||
- 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.
|
||||
|
||||
Controllers are classes which are responsible for application behavior. The recommended way of
|
||||
declaring controllers is using the array notation:
|
||||
- The `run` and `config` methods accept a function, which can also be injected with "service"
|
||||
components as dependencies.
|
||||
|
||||
- Controllers are defined by an 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) {
|
||||
@@ -215,28 +282,14 @@ someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope
|
||||
|
||||
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 of a for every instance of the `ng-controller`
|
||||
directive in the template.
|
||||
|
||||
### Factory methods
|
||||
Moreover, additional dependencies are made available to Controllers:
|
||||
|
||||
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', []).
|
||||
config(['depProvider', function(depProvider){
|
||||
...
|
||||
}]).
|
||||
factory('serviceId', ['depService', function(depService) {
|
||||
...
|
||||
}]).
|
||||
directive('directiveName', ['depService', function(depService) {
|
||||
...
|
||||
}]).
|
||||
filter('filterName', ['depService', function(depService) {
|
||||
...
|
||||
}]).
|
||||
run(['depService', function(depService) {
|
||||
...
|
||||
}]);
|
||||
```
|
||||
* {@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 servies 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.
|
||||
Reference in New Issue
Block a user