p.location-badge. exported from angular2/annotations defined in angular2/src/core/annotations_impl/annotations.js (line 371) :markdown Directives allow you to attach behavior to elements in the DOM. Directives with an embedded view are called Components. A directive consists of a single directive annotation and a controller class. When the directive's `selector` matches elements in the DOM, the following steps occur: 1. For each directive, the `ElementInjector` attempts to resolve the directive's constructor arguments. 2. Angular instantiates directives for each matched element using `ElementInjector` in a depth-first order, as declared in the HTML. ## Understanding How Injection Works There are three stages of injection resolution. - *Pre-existing Injectors*: - The terminal Injector cannot resolve dependencies. It either throws an error or, if the dependency was specified as `@Optional`, returns `null`. - The platform injector resolves browser singleton resources, such as: cookies, title, location, and others. - *Component Injectors*: Each `@Component` has its own Injector, and they follow the same parent-child hierarchy as the components in the DOM. - *Element Injectors*: Each component has a Shadow DOM. Within the Shadow DOM each element has an `ElementInjector` which follow the same parent-child hierarchy as the DOM elements themselves. When a template is instantiated, it also must instantiate the corresponding directives in a depth-first order. The current `ElementInjector` resolves the constructor dependencies for each directive. Angular then resolves dependencies as follows, according to the order in which they appear in the View: 1. Dependencies on the current element 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary 3. Dependencies on component injectors and their parents until it encounters the root component 4. Dependencies on pre-existing injectors The `ElementInjector` can inject other directives, element-specific special objects, or it can delegate to the parent injector. To inject other directives, declare the constructor parameter as: - `directive:DirectiveType`: a directive on the current element only - `@Ancestor() directive:DirectiveType`: any directive that matches the type between the current element and the Shadow DOM root. Current element is not included in the resolution, therefore even if it could resolve it, it will be ignored. - `@Parent() directive:DirectiveType`: any directive that matches the type on a direct parent element only. - `@Children query:Query`: A live collection of direct child directives (will be implemented in later release). - `@Descendants query:Query`: A live collection of any child directives (will be implemented in later relaese). To inject element-specific special objects, declare the constructor parameter as: - `element: ElementRef` to obtain a reference to logical element in the view. - `viewContainer: ViewContainerRef` to control child template instantiation, for Directive directives only - `bindingPropagation: BindingPropagation` to control change detection in a more granular way. ## Example The following example demonstrates how dependency injection resolves constructor arguments in practice. Assume this HTML template: ```
``` With the following `dependency` decorator and `SomeService` injectable class. ``` @Injectable() class SomeService { } @Directive({ selector: '[dependency]', properties: { 'id':'dependency' } }) class Dependency { id:string; } ``` Let's step through the different ways in which `MyDirective` could be declared... ### No injection Here the constructor is declared with no arguments, therefore nothing is injected into `MyDirective`. ``` @Directive({ selector: '[my-directive]' }) class MyDirective { constructor() { } } ``` This directive would be instantiated with no dependencies. ### Component-level injection Directives can inject any injectable instance from the closest component injector or any of its parents. Here, the constructor declares a parameter, `someService`, and injects the `SomeService` type from the parent component's injector. ``` @Directive({ selector: '[my-directive]' }) class MyDirective { constructor(someService: SomeService) { } } ``` This directive would be instantiated with a dependency on `SomeService`. ### Injecting a directive from the current element Directives can inject other directives declared on the current element. ``` @Directive({ selector: '[my-directive]' }) class MyDirective { constructor(dependency: Dependency) { expect(dependency.id).toEqual(3); } } ``` This directive would be instantiated with `Dependency` declared at the same element, in this case `dependency="3"`. ### Injecting a directive from a direct parent element Directives can inject other directives declared on a direct parent element. By definition, a directive with a `@Parent` annotation does not attempt to resolve dependencies for the current element, even if this would satisfy the dependency. ``` @Directive({ selector: '[my-directive]' }) class MyDirective { constructor(@Parent() dependency: Dependency) { expect(dependency.id).toEqual(2); } } ``` This directive would be instantiated with `Dependency` declared at the parent element, in this case `dependency="2"`. ### Injecting a directive from any ancestor elements Directives can inject other directives declared on any ancestor element (in the current Shadow DOM), i.e. on the parent element and its parents. By definition, a directive with an `@Ancestor` annotation does not attempt to resolve dependencies for the current element, even if this would satisfy the dependency. ``` @Directive({ selector: '[my-directive]' }) class MyDirective { constructor(@Ancestor() dependency: Dependency) { expect(dependency.id).toEqual(2); } } ``` Unlike the `@Parent` which only checks the parent, `@Ancestor` checks the parent, as well as its parents recursively. If `dependency="2"` didn't exist on the direct parent, this injection would have returned `dependency="1"`. ### Injecting a live collection of direct child directives A directive can also query for other child directives. Since parent directives are instantiated before child directives, a directive can't simply inject the list of child directives. Instead, the directive injects a QueryList, which updates its contents as children are added, removed, or moved by a directive that uses a ViewContainerRef such as a `for`, an `if`, or a `switch`. ``` @Directive({ selector: '[my-directive]' }) class MyDirective { constructor(@Query(Marker) dependencies:QueryList) { } } ``` This directive would be instantiated with a QueryList which contains `Dependency` 4 and 6. Here, `Dependency` 5 would not be included, because it is not a direct child. ### Injecting a live collection of descendant directives Note: This is will be implemented in later release. () Similar to `@Children` above, but also includes the children of the child elements. ``` @Directive({ selector: '[my-directive]' }) class MyDirective { constructor(@QueryDescendents(Marker) dependencies:QueryList) { } } ``` This directive would be instantiated with a Query which would contain `Dependency` 4, 5 and 6. ### Optional injection The normal behavior of directives is to return an error when a specified dependency cannot be resolved. If you would like to inject `null` on unresolved dependency instead, you can annotate that dependency with `@Optional()`. This explicitly permits the author of a template to treat some of the surrounding directives as optional. ``` @Directive({ selector: '[my-directive]' }) class MyDirective { constructor(@Optional() dependency:Dependency) { } } ``` This directive would be instantiated with a `Dependency` directive found on the current element. If none can be found, the injector supplies `null` instead of throwing an error. ## Example Here we use a decorator directive to simply define basic tool-tip behavior. ``` @Directive({ selector: '[tooltip]', properties: { 'text': 'tooltip' }, hostListeners: { 'onmouseenter': 'onMouseEnter()', 'onmouseleave': 'onMouseLeave()' } }) class Tooltip{ text:string; overlay:Overlay; // NOT YET IMPLEMENTED overlayManager:OverlayManager; // NOT YET IMPLEMENTED constructor(overlayManager:OverlayManager) { this.overlay = overlay; } onMouseEnter() { // exact signature to be determined this.overlay = this.overlayManager.open(text, ...); } onMouseLeave() { this.overlay.close(); this.overlay = null; } } ``` In our HTML template, we can then add this behavior to a `
` or any other element with the `tooltip` selector, like so: ```
``` Directives can also control the instantiation, destruction, and positioning of inline template elements: A directive uses a ViewContainerRef to instantiate, insert, move, and destroy views at runtime. The ViewContainerRef is created as a result of `