Class Overview
Class Description
Directives allow you to attach behavior to elements in the DOM.
DirectiveMetadata
s with an embedded view are called ComponentMetadata
s.
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:
- For each directive, the
ElementInjector
attempts to resolve the directive's constructor arguments. - 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
, returnsnull
. - The platform injector resolves browser singleton resources, such as: cookies, title, location, and others.
- The terminal
- Component Injectors: Each component instance has its own
Injector
, and they follow the same parent-child hierarchy as the component instances in the DOM. - Element Injectors: Each component instance 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
ComponentMetadata
:
- Dependencies on the current element
- Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary
- Dependencies on component injectors and their parents until it encounters the root component
- 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@Host() directive:DirectiveType
: any directive that matches the type between the current element and the Shadow DOM root.@Query(DirectiveType) query:QueryList<DirectiveType>
: A live collection of direct child directives.@QueryDescendants(DirectiveType) query:QueryList<DirectiveType>
: A live collection of any child directives.
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, forDirectiveMetadata
directives onlybindingPropagation: 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.
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
.
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.
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.
This directive would be instantiated with Dependency
declared at the same element, in this case
dependency="3"
.
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 current element, the parent element, or its parents.
@Host
checks the current element, 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 ngFor
, an
ngIf
, or an ngSwitch
.
This directive would be instantiated with a QueryList
which contains Dependency
4 and
Dependency
6. Here, Dependency
5 would not be included, because it is not a direct child.
Injecting a live collection of descendant directives
By passing the descendant flag to @Query
above, we can include the children of the child
elements.
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.
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.
In our HTML template, we can then add this behavior to a <div>
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 <template>
element, and represents a
location in the current view
where these actions are performed.
Views are always created as children of the current ComponentMetadata
, and as siblings of
the
<template>
element. Thus a
directive in a child view cannot inject the directive that created it.
Since directives that create views via ViewContainers are common in Angular, and using the full
<template>
element syntax is wordy, Angular
also supports a shorthand notation: <li *foo="bar">
and <li template="foo: bar">
are
equivalent.
Thus,
Expands in use to:
Notice that although the shorthand places *foo="bar"
within the <li>
element, the binding for
the directive
controller is correctly instantiated on the <template>
element rather than the <li>
element.
Lifecycle hooks
When the directive class implements some 生命周期钩子 the callbacks are called by the change detection at defined points in time during the life of the directive.
Example
Let's suppose we want to implement the unless
behavior, to conditionally include a template.
Here is a simple directive that triggers on an unless
selector:
We can then use this unless
selector in a template:
Once the directive instantiates the child view, the shorthand notation for the template expands and the result is:
Note also that although the <li></li>
template still exists inside the <template></template>
,
the instantiated
view occurs on the second <li></li>
which is a sibling to the <template>
element.
Constructor
Class Details
selector : string
The CSS selector that triggers the instantiation of a directive.
Angular only allows directives to trigger on CSS selectors that do not cross element boundaries.
selector
may be declared as one of the following:
element-name
: select by element name..class
: select by class name.[attribute]
: select by attribute name.[attribute=value]
: select by attribute name and value.:not(sub_selector)
: select only if the element does not match thesub_selector
.selector1, selector2
: select if eitherselector1
orselector2
matches.
Suppose we have a directive with an input[type=text]
selector.
And the following HTML:
The directive would only be instantiated on the <input type="text">
element.
inputs : string[]
Enumerates the set of data-bound input properties for a directive
Angular automatically updates input properties during change detection.
The inputs
property defines a set of directiveProperty
to bindingProperty
configuration:
directiveProperty
specifies the component property where the value is written.bindingProperty
specifies the DOM property where the value is read from.
When bindingProperty
is not provided, it is assumed to be equal to directiveProperty
.
The following example creates a component with two data-bound properties.
outputs : string[]
Enumerates the set of event-bound output properties.
When an output property emits an event, an event handler attached to that event the template is invoked.
The outputs
property defines a set of directiveProperty
to bindingProperty
configuration:
directiveProperty
specifies the component property that emits events.bindingProperty
specifies the DOM property the event handler is attached to.
host : {[key: string]: string}
Specify the events, actions, properties and attributes related to the host element.
Host Listeners
Specifies which DOM events a directive listens to via a set of (event)
to method
key-value pairs:
event
: the DOM event that the directive listens to.statement
: the statement to execute when the event occurs. If the evaluation of the statement returnsfalse
, thenpreventDefault
is applied on the DOM event.
To listen to global events, a target must be added to the event name.
The target can be window
, document
or body
.
When writing a directive event binding, you can also refer to the $event local variable.
The following example declares a directive that attaches a click listener to the button and counts clicks.
Host Property Bindings
Specifies which DOM properties a directive updates.
Angular automatically checks host property bindings during change detection. If a binding changes, it will update the host element of the directive.
The following example creates a directive that sets the valid
and invalid
classes
on the DOM element that has ngModel directive on it.
Attributes
Specifies static attributes that should be propagated to a host element.
In this example using my-button
directive (ex.: <div my-button></div>
) on a host element
(here: <div>
) will ensure that this element will get the "button" role.
providers : Provider[]
Defines the set of injectable objects that are visible to a Directive and its light DOM children.
Simple Example
Here is an example of a class that can be injected:
exportAs : string
Defines the name that can be used in the template to assign this directive to a variable.
Simple Example
queries : {[key: string]: any}
Configures the queries that will be injected into the directive.
Content queries are set before the ngAfterContentInit
callback is called.
View queries are set before the ngAfterViewInit
callback is called.
exported from @angular/core/index, defined in @angular/core/src/metadata/directives.ts