Change the appearance or behavior of DOM elements and Angular components with attribute directives.
Building an attribute directive
This section walks you through creating a highlight directive that sets the background color of the host element to yellow.
To create a directive, use the CLI command
ng generate directive
.ng generate directive highlight
The CLI creates
src/app/highlight.directive.ts
, a corresponding test filesrc/app/highlight.directive.spec.ts
.src/app/highlight.directive.ts
import {Directive} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective {}
The
@Directive()
decorator's configuration property specifies the directive's CSS attribute selector,[appHighlight]
.Import
ElementRef
from@angular/core
.ElementRef
grants direct access to the host DOM element through itsnativeElement
property.Add
ElementRef
in the directive'sconstructor()
to inject a reference to the host DOM element, the element to which you applyappHighlight
.Add logic to the
HighlightDirective
class that sets the background to yellow.src/app/highlight.directive.ts
import {Directive, ElementRef} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective { constructor(private el: ElementRef) { this.el.nativeElement.style.backgroundColor = 'yellow'; }}
HELPFUL: Directives do not support namespaces.
src/app/app.component.avoid.html (unsupported)
<p app:Highlight>This is invalid</p>
Applying an attribute directive
- To use the
HighlightDirective
, add a<p>
element to the HTML template with the directive as an attribute.src/app/app.component.html
<h1>My First Attribute Directive</h1><p appHighlight>Highlight me!</p><p appHighlight="yellow">Highlighted in yellow</p><p [appHighlight]="'orange'">Highlighted in orange</p><p [appHighlight]="color">Highlighted with parent component's color</p>
Angular creates an instance of the HighlightDirective
class and injects a reference to the <p>
element into the directive's constructor, which sets the <p>
element's background style to yellow.
Handling user events
This section shows you how to detect when a user mouses into or out of the element and to respond by setting or clearing the highlight color.
Import
HostListener
from '@angular/core'.src/app/highlight.directive.ts (imports)
import {Directive, ElementRef, HostListener} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective { constructor(private el: ElementRef) {} @HostListener('mouseenter') onMouseEnter() { this.highlight('yellow'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(''); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}
Add two event handlers that respond when the mouse enters or leaves, each with the
@HostListener()
decorator.src/app/highlight.directive.ts (mouse-methods)
import {Directive, ElementRef, HostListener} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective { constructor(private el: ElementRef) {} @HostListener('mouseenter') onMouseEnter() { this.highlight('yellow'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(''); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}
Subscribe to events of the DOM element that hosts an attribute directive, the <p>
in this case, with the @HostListener()
decorator.
HELPFUL: The handlers delegate to a helper method, highlight()
, that sets the color on the host DOM element, el
.
The complete directive is as follows:
src/app/highlight.directive.ts
import {Directive, ElementRef, HostListener} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective { constructor(private el: ElementRef) {} @HostListener('mouseenter') onMouseEnter() { this.highlight('yellow'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(''); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}
The background color appears when the pointer hovers over the paragraph element and disappears as the pointer moves out.
Passing values into an attribute directive
This section walks you through setting the highlight color while applying the HighlightDirective
.
In
highlight.directive.ts
, importInput
from@angular/core
.src/app/highlight.directive.ts (imports)
import {Directive, ElementRef, HostListener, Input} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective { constructor(private el: ElementRef) {} @Input() appHighlight = ''; @HostListener('mouseenter') onMouseEnter() { this.highlight(this.appHighlight || 'red'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(''); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}
Add an
appHighlight
@Input()
property.src/app/highlight.directive.ts
import {Directive, ElementRef, HostListener, Input} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective { constructor(private el: ElementRef) {} @Input() appHighlight = ''; @HostListener('mouseenter') onMouseEnter() { this.highlight(this.appHighlight || 'red'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(''); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}
The
@Input()
decorator adds metadata to the class that makes the directive'sappHighlight
property available for binding.In
app.component.ts
, add acolor
property to theAppComponent
.src/app/app.component.ts (class)
import {Component} from '@angular/core';import {HighlightDirective} from './highlight.directive';@Component({ selector: 'app-root', templateUrl: './app.component.1.html', imports: [HighlightDirective],})export class AppComponent { color = 'yellow';}
To simultaneously apply the directive and the color, use property binding with the
appHighlight
directive selector, setting it equal tocolor
.src/app/app.component.html (color)
<h1>My First Attribute Directive</h1><h2>Pick a highlight color</h2><div> <input type="radio" name="colors" (click)="color='lightgreen'">Green <input type="radio" name="colors" (click)="color='yellow'">Yellow <input type="radio" name="colors" (click)="color='cyan'">Cyan</div><p [appHighlight]="color">Highlight me!</p><p [appHighlight]="color" defaultColor="violet"> Highlight me too!</p><hr /><h2>Mouse over the following lines to see fixed highlights</h2><p [appHighlight]="'yellow'">Highlighted in yellow</p><p appHighlight="orange">Highlighted in orange</p><hr /><h2>ngNonBindable</h2><p>Use ngNonBindable to stop evaluation.</p><p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p><h3>ngNonBindable with a directive</h3><div ngNonBindable [appHighlight]="'yellow'">This should not evaluate: {{ 1 +1 }}, but will highlight yellow.</div>
The
[appHighlight]
attribute binding performs two tasks:- Applies the highlighting directive to the
<p>
element - Sets the directive's highlight color with a property binding
- Applies the highlighting directive to the
Setting the value with user input
This section guides you through adding radio buttons to bind your color choice to the appHighlight
directive.
Add markup to
app.component.html
for choosing a color as follows:src/app/app.component.html (v2)
<h1>My First Attribute Directive</h1><h2>Pick a highlight color</h2><div> <input type="radio" name="colors" (click)="color='lightgreen'">Green <input type="radio" name="colors" (click)="color='yellow'">Yellow <input type="radio" name="colors" (click)="color='cyan'">Cyan</div><p [appHighlight]="color">Highlight me!</p><p [appHighlight]="color" defaultColor="violet"> Highlight me too!</p><hr /><h2>Mouse over the following lines to see fixed highlights</h2><p [appHighlight]="'yellow'">Highlighted in yellow</p><p appHighlight="orange">Highlighted in orange</p><hr /><h2>ngNonBindable</h2><p>Use ngNonBindable to stop evaluation.</p><p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p><h3>ngNonBindable with a directive</h3><div ngNonBindable [appHighlight]="'yellow'">This should not evaluate: {{ 1 +1 }}, but will highlight yellow.</div>
Revise the
AppComponent.color
so that it has no initial value.src/app/app.component.ts (class)
import {Component} from '@angular/core';import {HighlightDirective} from './highlight.directive';@Component({ selector: 'app-root', templateUrl: './app.component.html', imports: [HighlightDirective],})export class AppComponent { color = '';}
In
highlight.directive.ts
, reviseonMouseEnter
method so that it first tries to highlight withappHighlight
and falls back tored
ifappHighlight
isundefined
.src/app/highlight.directive.ts (mouse-enter)
import {Directive, ElementRef, HostListener, Input} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective { constructor(private el: ElementRef) {} @Input() appHighlight = ''; @HostListener('mouseenter') onMouseEnter() { this.highlight(this.appHighlight || 'red'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(''); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}
Serve your application to verify that the user can choose the color with the radio buttons.
Binding to a second property
This section guides you through configuring your application so the developer can set the default color.
Add a second
Input()
property toHighlightDirective
calleddefaultColor
.src/app/highlight.directive.ts (defaultColor)
import {Directive, ElementRef, HostListener, Input} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective { constructor(private el: ElementRef) {} @Input() defaultColor = ''; @Input() appHighlight = ''; @HostListener('mouseenter') onMouseEnter() { this.highlight(this.appHighlight || this.defaultColor || 'red'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(''); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}
Revise the directive's
onMouseEnter
so that it first tries to highlight with theappHighlight
, then with thedefaultColor
, and falls back tored
if both properties areundefined
.src/app/highlight.directive.ts (mouse-enter)
import {Directive, ElementRef, HostListener, Input} from '@angular/core';@Directive({ selector: '[appHighlight]',})export class HighlightDirective { constructor(private el: ElementRef) {} @Input() defaultColor = ''; @Input() appHighlight = ''; @HostListener('mouseenter') onMouseEnter() { this.highlight(this.appHighlight || this.defaultColor || 'red'); } @HostListener('mouseleave') onMouseLeave() { this.highlight(''); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }}
To bind to the
AppComponent.color
and fall back to "violet" as the default color, add the following HTML. In this case, thedefaultColor
binding doesn't use square brackets,[]
, because it is static.src/app/app.component.html (defaultColor)
<h1>My First Attribute Directive</h1><h2>Pick a highlight color</h2><div> <input type="radio" name="colors" (click)="color='lightgreen'">Green <input type="radio" name="colors" (click)="color='yellow'">Yellow <input type="radio" name="colors" (click)="color='cyan'">Cyan</div><p [appHighlight]="color">Highlight me!</p><p [appHighlight]="color" defaultColor="violet"> Highlight me too!</p><hr /><h2>Mouse over the following lines to see fixed highlights</h2><p [appHighlight]="'yellow'">Highlighted in yellow</p><p appHighlight="orange">Highlighted in orange</p><hr /><h2>ngNonBindable</h2><p>Use ngNonBindable to stop evaluation.</p><p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p><h3>ngNonBindable with a directive</h3><div ngNonBindable [appHighlight]="'yellow'">This should not evaluate: {{ 1 +1 }}, but will highlight yellow.</div>
As with components, you can add multiple directive property bindings to a host element.
The default color is red if there is no default color binding. When the user chooses a color the selected color becomes the active highlight color.
Deactivating Angular processing with NgNonBindable
To prevent expression evaluation in the browser, add ngNonBindable
to the host element.
ngNonBindable
deactivates interpolation, directives, and binding in templates.
In the following example, the expression {{ 1 + 1 }}
renders just as it does in your code editor, and does not display 2
.
src/app/app.component.html
<h1>My First Attribute Directive</h1><h2>Pick a highlight color</h2><div> <input type="radio" name="colors" (click)="color='lightgreen'">Green <input type="radio" name="colors" (click)="color='yellow'">Yellow <input type="radio" name="colors" (click)="color='cyan'">Cyan</div><p [appHighlight]="color">Highlight me!</p><p [appHighlight]="color" defaultColor="violet"> Highlight me too!</p><hr /><h2>Mouse over the following lines to see fixed highlights</h2><p [appHighlight]="'yellow'">Highlighted in yellow</p><p appHighlight="orange">Highlighted in orange</p><hr /><h2>ngNonBindable</h2><p>Use ngNonBindable to stop evaluation.</p><p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p><h3>ngNonBindable with a directive</h3><div ngNonBindable [appHighlight]="'yellow'">This should not evaluate: {{ 1 +1 }}, but will highlight yellow.</div>
Applying ngNonBindable
to an element stops binding for that element's child elements.
However, ngNonBindable
still lets directives work on the element where you apply ngNonBindable
.
In the following example, the appHighlight
directive is still active but Angular does not evaluate the expression {{ 1 + 1 }}
.
src/app/app.component.html
<h1>My First Attribute Directive</h1><h2>Pick a highlight color</h2><div> <input type="radio" name="colors" (click)="color='lightgreen'">Green <input type="radio" name="colors" (click)="color='yellow'">Yellow <input type="radio" name="colors" (click)="color='cyan'">Cyan</div><p [appHighlight]="color">Highlight me!</p><p [appHighlight]="color" defaultColor="violet"> Highlight me too!</p><hr /><h2>Mouse over the following lines to see fixed highlights</h2><p [appHighlight]="'yellow'">Highlighted in yellow</p><p appHighlight="orange">Highlighted in orange</p><hr /><h2>ngNonBindable</h2><p>Use ngNonBindable to stop evaluation.</p><p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p><h3>ngNonBindable with a directive</h3><div ngNonBindable [appHighlight]="'yellow'">This should not evaluate: {{ 1 +1 }}, but will highlight yellow.</div>
If you apply ngNonBindable
to a parent element, Angular disables interpolation and binding of any sort, such as property binding or event binding, for the element's children.