diff options
| author | Misko Hevery | 2012-02-27 12:14:48 -0800 | 
|---|---|---|
| committer | Misko Hevery | 2012-06-02 16:02:05 -0700 | 
| commit | 2e90cdc3d4dff966faed97d3a566e9d9c00c9794 (patch) | |
| tree | 413ba0078f5decf7026791177dfcf8e3ff5f0662 /docs/content/guide/di.ngdoc | |
| parent | 581f93ae56ebb0086d0c0989df69a28656395006 (diff) | |
| download | angular.js-2e90cdc3d4dff966faed97d3a566e9d9c00c9794.tar.bz2 | |
docs(dependency injecton): rewrite
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  | 
