Attribute Directives
Attribute Directives
Try the .
{@a directive-overview}
Directives overview
There are three kinds of directives in Angular:
Components are the most common of the three directives. You saw a component for the first time in the
QuickStart guide.
Structural Directives change the structure of the view. Two examples are NgFor and NgIf. Learn about them in
the Structural Directives guide.
Attribute directives are used as attributes of elements. The built-in NgStyle directive in the Template Syntax
guide, for example, can change several element styles at the same time.
This page demonstrates building a simple appHighlight attribute directive to set an element's background color
when the user hovers over that element. You can apply it like this:
{@a write-directive}
After the @Directive metadata comes the directive's controller class, called HighlightDirective ,
which contains the (currently empty) logic for the directive. Exporting HighlightDirective makes the
directive accessible.
The import statement specifies an additional ElementRef symbol from the Angular core library:
You use the ElementRef in the directive's constructor to inject a reference to the host DOM element, the
element to which you applied appHighlight .
ElementRef grants direct access to the host DOM element through its nativeElement property.
This first implementation sets the background color of the host element to yellow.
{@a apply-directive}
ng serve
To summarize, Angular found the appHighlight attribute on the host <p> element. It created an
instance of the HighlightDirective class and injected a reference to the <p> element into the
directive's constructor which sets the <p> element's background style to yellow.
{@a respond-to-user}
Then add two eventhandlers that respond when the mouse enters or leaves, each adorned by the
HostListener decorator.
The @HostListener decorator lets you subscribe to events of the DOM element that hosts an attribute
directive, the <p> in this case.
Of course you could reach into the DOM with standard JavaScript and attach event listeners manually. There
are at least three problems with _that_ approach: 1. You have to write the listeners correctly. 1. The code must
*detach* the listener when the directive is destroyed to avoid memory leaks. 1. Talking to DOM API directly isn't
a best practice.
The handlers delegate to a helper method that sets the color on the host DOM element, el .
The helper method, highlight , was extracted from the constructor. The revised constructor simply
declares the injected el: ElementRef .
Run the app and confirm that the background color appears when the mouse hovers over the p and
disappears as it moves out.
{@a bindings}
{@a input}
Notice the @Input decorator. It adds metadata to the class that makes the directive's highlightColor
property available for binding.
It's called an input property because data flows from the binding expression into the directive. Without that
input metadata, Angular rejects the binding; see below for more about that.
Try it by adding the following directive binding variations to the AppComponent template:
That's good, but it would be nice to simultaneously apply the directive and set the color in the same attribute
like this.
The [appHighlight] attribute binding both applies the highlighting directive to the <p> element and
sets the directive's highlight color with a property binding. You're re-using the directive's attribute selector
( [appHighlight] ) to do both jobs. That's a crisp, compact syntax.
You'll have to rename the directive's highlightColor property to appHighlight because that's now
the color property binding name.
This is disagreeable. The word, appHighlight , is a terrible property name and it doesn't convey the
property's intent.
{@a input-alias}
Bind to an @Input alias
Fortunately you can name the directive property whatever you want and alias it for binding purposes.
Restore the original property name and specify the selector as the alias in the argument to @Input .
Inside the directive the property is known as highlightColor . Outside the directive, where you bind to it,
it's known as appHighlight .
You get the best of both worlds: the property name you want and the binding syntax you want:
Now that you're binding via the alias to the highlightColor , modify the onMouseEnter() method to
use that property. If someone neglects to bind to appHighlightColor , highlight the host element in red:
{@a second-property}
Revise the directive's onMouseEnter so that it first tries to highlight with the highlightColor , then
with the defaultColor , and falls back to "red" if both properties are undefined.
How do you bind to a second property when you're already binding to the appHighlight attribute name?
As with components, you can add as many directive property bindings as you need by stringing them along in
the template. The developer should be able to write the following template HTML to both bind to the
AppComponent.color and fall back to "violet" as the default color.
Angular knows that the defaultColor binding belongs to the HighlightDirective because you
made it public with the @Input decorator.
Here's how the harness should work when you're done coding.
Summary
This page covered how to:
In this demo, the highlightColor property is an input property of the HighlightDirective . You've
seen it applied without an alias:
Either way, the @Input decorator tells Angular that this property is public and available for binding by a
parent component. Without @Input , Angular refuses to bind to the property.
You've bound template HTML to component properties before and never used @Input . What's different?
The difference is a matter of trust. Angular treats a component's template as belonging to the component. The
component and its template trust each other implicitly. Therefore, the component's own template may bind to
any property of that component, with or without the @Input decorator.
But a component or directive shouldn't blindly trust other components and directives. The properties of a
component or directive are hidden from binding by default. They are private from an Angular binding
perspective. When adorned with the @Input decorator, the property becomes public from an Angular
binding perspective. Only then can it be bound by some other component or directive.
You can tell if @Input is needed by the position of the property name in a binding.
When it appears in the template expression to the right of the equals (=), it belongs to the template's
component and does not require the @Input decorator.
When it appears in square brackets ([ ]) to the left of the equals (=), the property belongs to some other
component or directive; that property must be adorned with the @Input decorator.
The color property in the expression on the right belongs to the template's component. The template
and its component trust each other. The color property doesn't require the @Input decorator.
The appHighlight property on the left refers to an aliased property of the HighlightDirective ,
not a property of the template's component. There are trust issues. Therefore, the directive property must
carry the @Input decorator.