diff options
Diffstat (limited to 'docs/content/guide/di.ngdoc')
| -rw-r--r-- | docs/content/guide/di.ngdoc | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/docs/content/guide/di.ngdoc b/docs/content/guide/di.ngdoc new file mode 100644 index 00000000..d75d5b09 --- /dev/null +++ b/docs/content/guide/di.ngdoc @@ -0,0 +1,234 @@ +@ngdoc overview +@name Developer Guide: Dependency Injection +@description + +# Dependency Injection + +Dependency Injection (DI) is a software design pattern that deals with how code gets hold of its +dependencies. + +For in-depth discussion about DI, see {@link http://en.wikipedia.org/wiki/Dependency_injection +Dependency Injection} at Wikipedia, {@link http://martinfowler.com/articles/injection.html +Inversion of Control} by Martin Fowler, or read about DI in your favorite software design pattern +book. + +## DI in a nutshell + +There are only three ways how 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. + + +The first two option of creating or looking up dependencies are not optimal, because they hard +code the dependency, making 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. + +<pre> + function SomeClass(greeter) { + this.greeter = greeter + } + + SomeClass.prototype.doSomething = function(name) { + this.greeter.greet(name); + } +</pre> + +In the above example the `SomeClass` is not concerned with locating the `greeter` dependency, it +is simply handed the `greeter` at runtime. + +This is desirable, but it puts the responsibility of getting hold of the dependency onto the +code responsible for the construction of `SomeClass`. + +To manage the responsibility of dependency creation, each angular application has an {@link +api/angular.injector injector}. The injector is a service locator that is responsible for +construction and lookup of dependencies. + + +Here is an example of using the injector service. +<pre> + // Provide the wiring information in a module + 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); + } + }; + }). + + // New injector is created from the module. + // (This is usually done automatically by angular bootstrap) + var injector = angular.injector('myModule'); + + // Request any dependency from the injector + var greeter = injector.get('greeter'); +</pre> + +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 {@link +http://en.wikipedia.org/wiki/Law_of_Demeter Law of Demeter}. To remedy this, we turn the +dependency lookup responsibility to the injector by declaring the dependencies as in this example: + +<pre> + <!-- Given this HTML --> + <div ng-controller="MyController"> + <button ng-click="sayHello()">Hello</button> + </div> +</pre> +<pre> + // And this controller definition + function MyController($scope, greeter) { + $scope.sayHello = function() { + greeter('Hello World'); + }; + } + + // The 'ng-controller' directive does this behind the scenes + injector.instantiate(MyController); +</pre> + +Notice that by having the `ng-controller` instantiate the class, it can satisfy all of the +dependencies of the `MyController` without the controller ever knowing about the injector. This is +the best outcome. The application code simply ask for 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? + +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. + +# Inferring Dependencies + +The simplest way to get hold of the dependencies, is to assume that the function parameter names +are the names of the dependencies. + +<pre> + function MyController($scope, greeter) { + ... + } +</pre> + +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 {@link +http://www.pretotyping.org/ pretotyping}, and demo applications. + +# `$inject` Annotation + +To allow the minifers to rename the function parameters and still be able to inject right services +the function needs to be annotate with the `$inject` property. The `$inject` property is an array +of service names to inject. + +<pre> + var MyController = function(renamed$scope, renamedGreeter) { + ... + } + MyController.$inject = ['$scope', 'greeter']; +</pre> + +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 + +Sometimes using the `$inject` annotation style is not convenient such as when annotating +directives. + +For example: +<pre> + someModule.factory('greeter', function($window) { + ...; + }); +</pre> + +Results in code bloat do to the need of temporary variable: +<pre> + var greeterFactory = function(renamed$window) { + ...; + }; + + greeterFactory.$inject = ['$window']; + + someModule.factory('greeter', greeterFactory); +</pre> + +For this reason the third annotation style is provided as well. +<pre> + someModule.factory('greeter', ['$window', function(renamed$window) { + ...; + }]); +</pre> + +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. It is typically used in controllers and factory methods. + +## DI in controllers + +Controllers are classes which are responsible for application behavior. Recommended way of +declaring controllers is: + +<pre> + var MyController = function(dep1, dep2) { + ... + } + MyController.$inject = ['dep1', 'dep2']; + + MyController.prototype.aMethod = function() { + ... + } +</pre> + + +## Factory methods + +Factory methods are responsible for creating most objects in Angular. Examples are directives, +services, and filters. The factory methods are register with the module, and the recommended way +of declaring factories is: + +<pre> + angualar.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) { + ... + }]); +</pre>
\ No newline at end of file |
