An Attribute directive changes the appearance or behavior of a DOM element.
- Directives overview
- Build a simple attribute directive
- Apply the attribute directive to an element in a template
- Respond to user-initiated events
- Pass values into the directive with an @Input data binding
- Bind to a second property
There are three kinds of directives in Angular:
- Components—directives with a template.
- Structural directives—change the DOM layout by adding and removing DOM elements.
- Attribute directives—change the appearance or behavior of an element.
Components are the most common of the three directives. You saw a component for the first time in the QuickStart example.
Structural Directives change the structure of the view. Two examples are NgFor and NgIf in the Template Syntax page.
Attribute directives are used as attributes of elements. The built-in NgStyle directive in the Template Syntax page, for example, can change several element styles at the same time.
Build a simple attribute directive
An attribute directive minimally requires building a controller class annotated with
@Directive, which specifies the selector that identifies
The controller class implements the desired directive behavior.
This page demonstrates building a simple attribute directive to set an element's background color when the user hovers over that element.
Technically, a directive isn't necessary to simply set the background color. Style binding can set styles as follows:
For a simple example, though, this will demonstrate how attribute directives work.
Write the directive code
Follow the setup instructions for creating a new project named attribute-directives.
Create the following source file in the indicated folder with the following code:
import statement specifies symbols from the Angular
Directiveprovides the functionality of the
ElementRefinjects into the directive's constructor so the code can access the DOM element.
Inputallows data to flow from the binding expression into the directive.
@Directive decorator function contains the directive metadata in a configuration object
as an argument.
@Directive requires a CSS selector to identify
the HTML in the template that is associated with the directive.
The CSS selector for an attribute
is the attribute name in square brackets.
Here, the directive's selector is
Angular will locate all elements in the template that have an attribute named
Why not call it "highlight"?
Though highlight is a more concise name than myHighlight and would work, a best practice is to prefix selector names to ensure they don't conflict with standard HTML attributes. This also reduces the risk colliding with third-party directive names.
Make sure you do not prefix the
highlight directive name with
that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose. For a simple demo, the short prefix,
my, helps distinguish your custom directive.
@Directive metadata comes the directive's controller class, called
HighlightDirective, which contains the logic for the directive. Exporting
HighlightDirective makes it accessible to other components.
Angular creates a new instance of the directive's controller class for
each matching element, injecting an Angular
into the constructor.
ElementRef is a service that grants direct access to the DOM element
Apply the attribute directive
To use the new
HighlightDirective, create a template that
applies the directive as an attribute to a paragraph (
In Angular terms, the
<p> element will be the attribute host.
Put the template in its own
app.component.htmlfile that looks like this:
Now reference this template in the
Next, add an
import statement to fetch the
Highlight directive and
add that class to the
declarations NgModule metadata. This way Angular
recognizes the directive when it encounters
myHighlight in the template.
Now when the app runs, the
myHighlight directive highlights the paragraph text.
Your directive isn't working?
Did you remember to add the directive to the
declarations attribute of
@NgModule? It is easy to forget!
Open the console in the browser tools and look for an error like this:
Angular detects that you're trying to bind to something but it can't find this directive
in the module's
HighlightDirective in the
Angular knows it can apply the directive to components declared in this module.
To summarize, Angular found the
myHighlight attribute on the
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.
Respond to user-initiated events
myHighlight simply sets an element color.
The directive could be more dynamic.
It could detect when the user mouses into or out of the element
and respond by setting or clearing the highlight color.
Begin by adding
HostListener to the list of imported symbols;
Import symbol as well because you'll need it soon.
Then add two eventhandlers that respond when the mouse enters or leaves, each adorned by the
@HostListener decorator lets you subscribe to events of the DOM element that hosts an attribute directive, the
<p> in this case.
- You have to write the listeners correctly.
- The code must detach the listener when the directive is destroyed to avoid memory leaks.
- Talking to DOM API directly isn't a best practice.
The handlers delegate to a helper method that sets the color on the DOM element,
which you declare and initialize in the constructor.
Here's the updated directive in full:
Run the app and confirm that the background color appears when the mouse hovers over the
disappears as it moves out.
Pass values into the directive with an @Input data binding
Currently the highlight color is hard-coded within the directive. That's inflexible. Let the user of the directive set the color in the template with a binding.
Start by adding a
highlightColor property to the directive class like this:
Binding to an @Input property
@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
color property to the
Let it control the highlight color with a property binding.
That's good, but it would be nice to simultaneously apply the directive and set the color in the same attribute like this.
[myHighlight] attribute binding both applies the highlighting directive to the
and sets the directive's highlight color with a property binding.
You're re-using the directive's attribute selector (
[myHighlight]) to do both jobs.
That's a crisp, compact syntax.
You'll have to rename the directive's
highlightColor property to
myHighlight because that's now the color property binding name.
This is disagreeable. The word,
myHighlight, is a terrible property name and it doesn't convey the property's intent.
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
Inside the directive the property is known as
Outside the directive, where you bind to it, it's known as
You get the best of both worlds: the property name you want and the binding syntax you want:
Now that you're binding to
highlightColor, modify the
onMouseEnter() method to use it.
If someone neglects to bind to
highlightColor, highlight in "red" by default.
Here's the latest version of the directive class.
Write a harness to try it
It may be difficult to imagine how this directive actually works.
In this section, you'll turn
AppComponent into a harness that
lets you pick the highlight color with a radio button and bind your color choice to the directive.
app.component.html as follows:
AppComponent.color so that it has no initial value.
Here is the harness and directive in action.
Bind to a second property
This highlight directive has a single customizable property. In a real app, it may need more.
At the moment, the default color — the color that prevails until the user picks a highlight color — is hard-coded as "red". Let the template developer set the default color.
Add a second input property to
Revise the directive's
onMouseEnter so that it first tries to highlight with the
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
myHighlight 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
and fall back to "violet" as the default color.
Angular knows that the
defaultColor binding belongs to the
because you made it public with the
Here's how the harness should work when you're done coding.
This page covered how to:
- Build an attribute directive that modifies the behavior of an element.
- Apply the directive to an element in a template.
- Respond to events that change the directive's behavior.
- Bind values to the directive.
The final source code follows:
You can also experience and download the
Appendix: Why add @Input?
In this demo, the
hightlightColor property is an input property of
HighlightDirective. You've seen it applied without an alias:
You've seen it with an alias:
Either way, the
@Input decorator tells Angular that this property is
public and available for binding by a parent component.
@Input, Angular refuses to bind to the property.
You've bound template HTML to component properties before and never used
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
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
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
Now apply that reasoning to the following example:
colorproperty in the expression on the right belongs to the template's component. The template and its component trust each other. The
colorproperty doesn't require the
myHighlightproperty on the left refers to an aliased property of the
MyHighlightDirective, not a property of the template's component. There are trust issues. Therefore, the directive property must carry the