Online

Attribute directives

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.

  1. To create a directive, use the CLI command ng generate directive.

``shell ng generate directive highlight ``

The CLI creates src/app/highlight.directive.ts, a corresponding test file src/app/highlight.directive.spec.ts.

```angular-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].

ElementRef grants direct access to the host DOM element through its nativeElement property.

  1. Import ElementRef and inject from @angular/core.
  1. Use inject to obtain a reference to the host DOM element, the element to which you apply appHighlight.
  1. Add logic to the HighlightDirective class that sets the background to yellow.
highlight.directive.ts
// #docregion
import {Directive, ElementRef, inject} from '@angular/core';

@Directive({
  selector: '[appHighlight]',
})
export class HighlightDirective {
  private el = inject(ElementRef);

  constructor() {
    this.el.nativeElement.style.backgroundColor = 'yellow';
  }
}
AVOID
<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.

app.component.html
<!-- #docregion -->
<h1>My First Attribute Directive</h1>
<!-- #docregion applied -->
<p appHighlight>Highlight me!</p>
<!-- #enddocregion applied  -->

<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, which uses inject(ElementRef) to get a reference to the <p> element and set its 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.

  1. Configure host event bindings using the host property in the @Directive() decorator.
src/app/highlight.directive.ts (decorator)
// #docplaster
// #docregion imports
import {Directive, ElementRef, inject} from '@angular/core';
// #enddocregion imports
// #docregion

// #docregion decorator
@Directive({
  selector: '[appHighlight]',
  host: {
    '(mouseenter)': 'onMouseEnter()',
    '(mouseleave)': 'onMouseLeave()',
  },
})
// #enddocregion decorator
export class HighlightDirective {
  private el = inject(ElementRef);

  // #docregion mouse-methods
  onMouseEnter() {
    this.highlight('yellow');
  }

  onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
  // #enddocregion mouse-methods
}
// #enddocregion
  1. Add two event handler methods, and map host element events to them via the host property.
highlight.directive.ts (mouse-methods)
// #docplaster
// #docregion imports
import {Directive, ElementRef, inject} from '@angular/core';
// #enddocregion imports
// #docregion

// #docregion decorator
@Directive({
  selector: '[appHighlight]',
  host: {
    '(mouseenter)': 'onMouseEnter()',
    '(mouseleave)': 'onMouseLeave()',
  },
})
// #enddocregion decorator
export class HighlightDirective {
  private el = inject(ElementRef);

  // #docregion mouse-methods
  onMouseEnter() {
    this.highlight('yellow');
  }

  onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
  // #enddocregion mouse-methods
}
// #enddocregion

Subscribe to events of the DOM element that hosts an attribute directive (the <p> in this case) by configuring event listeners on the directive's host property.

The complete directive is as follows:

highlight.directive.ts
// #docplaster
// #docregion imports
import {Directive, ElementRef, inject} from '@angular/core';
// #enddocregion imports
// #docregion

// #docregion decorator
@Directive({
  selector: '[appHighlight]',
  host: {
    '(mouseenter)': 'onMouseEnter()',
    '(mouseleave)': 'onMouseLeave()',
  },
})
// #enddocregion decorator
export class HighlightDirective {
  private el = inject(ElementRef);

  // #docregion mouse-methods
  onMouseEnter() {
    this.highlight('yellow');
  }

  onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
  // #enddocregion mouse-methods
}
// #enddocregion

The background color appears when the pointer hovers over the paragraph element and disappears as the pointer moves out.

Second Highlight

Passing values into an attribute directive

This section walks you through setting the highlight color while applying the HighlightDirective.

  1. In highlight.directive.ts, import input from @angular/core.
highlight.directive.ts (imports)
// #docregion, imports
import {Directive, ElementRef, inject, input} from '@angular/core';
// #enddocregion imports

@Directive({
  selector: '[appHighlight]',
  host: {
    '(mouseenter)': 'onMouseEnter()',
    '(mouseleave)': 'onMouseLeave()',
  },
})
export class HighlightDirective {
  private el = inject(ElementRef);

  // #docregion input
  appHighlight = input('');
  // #enddocregion input

  // #docregion mouse-enter
  onMouseEnter() {
    this.highlight(this.appHighlight() || 'red');
  }
  // #enddocregion mouse-enter

  onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}
  1. Add an appHighlight input property.
highlight.directive.ts
// #docregion, imports
import {Directive, ElementRef, inject, input} from '@angular/core';
// #enddocregion imports

@Directive({
  selector: '[appHighlight]',
  host: {
    '(mouseenter)': 'onMouseEnter()',
    '(mouseleave)': 'onMouseLeave()',
  },
})
export class HighlightDirective {
  private el = inject(ElementRef);

  // #docregion input
  appHighlight = input('');
  // #enddocregion input

  // #docregion mouse-enter
  onMouseEnter() {
    this.highlight(this.appHighlight() || 'red');
  }
  // #enddocregion mouse-enter

  onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}

The input() function adds metadata to the class that makes the directive's appHighlight property available for binding.

  1. In app.component.ts, add a color property to the AppComponent.
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],
})
// #docregion class
export class AppComponent {
  color = 'yellow';
}
  1. To simultaneously apply the directive and the color, use property binding with the appHighlight directive selector, setting it equal to color.
app.component.html (color)
<!-- #docregion 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>
<!-- #docregion color -->
<p [appHighlight]="color">Highlight me!</p>
<!-- #enddocregion color, v2 -->

<!-- #docregion defaultColor -->
<p [appHighlight]="color" defaultColor="violet">Highlight me too!</p>
<!-- #enddocregion defaultColor, -->

<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>
<!-- #docregion ngNonBindable -->
<p>Use ngNonBindable to stop evaluation.</p>
<p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p>
<!-- #enddocregion ngNonBindable -->

<!-- #docregion ngNonBindable-with-directive -->
<h3>ngNonBindable with a directive</h3>
<div ngNonBindable [appHighlight]="'yellow'">
  This should not evaluate: {{ 1 + 1 }}, but will highlight yellow.
</div>
<!-- #enddocregion ngNonBindable-with-directive -->

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

Setting the value with user input

This section guides you through adding radio buttons to bind your color choice to the appHighlight directive.

  1. Add markup to app.component.html for choosing a color as follows:
app.component.html (v2)
<!-- #docregion 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>
<!-- #docregion color -->
<p [appHighlight]="color">Highlight me!</p>
<!-- #enddocregion color, v2 -->

<!-- #docregion defaultColor -->
<p [appHighlight]="color" defaultColor="violet">Highlight me too!</p>
<!-- #enddocregion defaultColor, -->

<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>
<!-- #docregion ngNonBindable -->
<p>Use ngNonBindable to stop evaluation.</p>
<p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p>
<!-- #enddocregion ngNonBindable -->

<!-- #docregion ngNonBindable-with-directive -->
<h3>ngNonBindable with a directive</h3>
<div ngNonBindable [appHighlight]="'yellow'">
  This should not evaluate: {{ 1 + 1 }}, but will highlight yellow.
</div>
<!-- #enddocregion ngNonBindable-with-directive -->
  1. Revise the AppComponent.color so that it has no initial value.
app.component.ts (class)
// #docregion
import {Component} from '@angular/core';
import {HighlightDirective} from './highlight.directive';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  imports: [HighlightDirective],
})
// #docregion class
export class AppComponent {
  color = '';
}
  1. In highlight.directive.ts, revise onMouseEnter method so that it first tries to highlight with appHighlight and falls back to red if appHighlight is undefined.
highlight.directive.ts (mouse-enter)
// #docregion, imports
import {Directive, ElementRef, inject, input} from '@angular/core';
// #enddocregion imports

@Directive({
  selector: '[appHighlight]',
  host: {
    '(mouseenter)': 'onMouseEnter()',
    '(mouseleave)': 'onMouseLeave()',
  },
})
export class HighlightDirective {
  private el = inject(ElementRef);

  // #docregion input
  appHighlight = input('');
  // #enddocregion input

  // #docregion mouse-enter
  onMouseEnter() {
    this.highlight(this.appHighlight() || 'red');
  }
  // #enddocregion mouse-enter

  onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}
  1. Serve your application to verify that the user can choose the color with the radio buttons.
Animated gif of the refactored highlight directive changing color according to the radio button the user selects

Binding to a second property

This section guides you through configuring your application so the developer can set the default color.

  1. Add a second input() property to HighlightDirective called defaultColor.
highlight.directive.ts (defaultColor)
import {Directive, ElementRef, inject, input} from '@angular/core';

@Directive({
  selector: '[appHighlight]',
  host: {
    '(mouseenter)': 'onMouseEnter()',
    '(mouseleave)': 'onMouseLeave()',
  },
})
export class HighlightDirective {
  private el = inject(ElementRef);

  // #docregion defaultColor
  defaultColor = input('');
  // #enddocregion defaultColor

  appHighlight = input('');

  // #docregion mouse-enter
  onMouseEnter() {
    this.highlight(this.appHighlight() || this.defaultColor() || 'red');
  }
  // #enddocregion mouse-enter

  onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}
  1. Revise the directive's onMouseEnter so that it first tries to highlight with the appHighlight, then with the defaultColor, and falls back to red if both properties are undefined.
highlight.directive.ts (mouse-enter)
import {Directive, ElementRef, inject, input} from '@angular/core';

@Directive({
  selector: '[appHighlight]',
  host: {
    '(mouseenter)': 'onMouseEnter()',
    '(mouseleave)': 'onMouseLeave()',
  },
})
export class HighlightDirective {
  private el = inject(ElementRef);

  // #docregion defaultColor
  defaultColor = input('');
  // #enddocregion defaultColor

  appHighlight = input('');

  // #docregion mouse-enter
  onMouseEnter() {
    this.highlight(this.appHighlight() || this.defaultColor() || 'red');
  }
  // #enddocregion mouse-enter

  onMouseLeave() {
    this.highlight('');
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}

In this case, the defaultColor binding doesn't use square brackets, [], because the value is a static string, not a dynamic expression.

  1. To bind to the AppComponent.color and fall back to "violet" as the default color, add the following HTML.
app.component.html (defaultColor)
<!-- #docregion 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>
<!-- #docregion color -->
<p [appHighlight]="color">Highlight me!</p>
<!-- #enddocregion color, v2 -->

<!-- #docregion defaultColor -->
<p [appHighlight]="color" defaultColor="violet">Highlight me too!</p>
<!-- #enddocregion defaultColor, -->

<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>
<!-- #docregion ngNonBindable -->
<p>Use ngNonBindable to stop evaluation.</p>
<p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p>
<!-- #enddocregion ngNonBindable -->

<!-- #docregion ngNonBindable-with-directive -->
<h3>ngNonBindable with a directive</h3>
<div ngNonBindable [appHighlight]="'yellow'">
  This should not evaluate: {{ 1 + 1 }}, but will highlight yellow.
</div>
<!-- #enddocregion ngNonBindable-with-directive -->

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.

Animated gif of final highlight directive that shows red color with no binding and violet with the default color set. When user selects color, the selection takes precedence.

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.

app.component.html
<!-- #docregion 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>
<!-- #docregion color -->
<p [appHighlight]="color">Highlight me!</p>
<!-- #enddocregion color, v2 -->

<!-- #docregion defaultColor -->
<p [appHighlight]="color" defaultColor="violet">Highlight me too!</p>
<!-- #enddocregion defaultColor, -->

<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>
<!-- #docregion ngNonBindable -->
<p>Use ngNonBindable to stop evaluation.</p>
<p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p>
<!-- #enddocregion ngNonBindable -->

<!-- #docregion ngNonBindable-with-directive -->
<h3>ngNonBindable with a directive</h3>
<div ngNonBindable [appHighlight]="'yellow'">
  This should not evaluate: {{ 1 + 1 }}, but will highlight yellow.
</div>
<!-- #enddocregion ngNonBindable-with-directive -->

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 }}.

app.component.html
<!-- #docregion 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>
<!-- #docregion color -->
<p [appHighlight]="color">Highlight me!</p>
<!-- #enddocregion color, v2 -->

<!-- #docregion defaultColor -->
<p [appHighlight]="color" defaultColor="violet">Highlight me too!</p>
<!-- #enddocregion defaultColor, -->

<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>
<!-- #docregion ngNonBindable -->
<p>Use ngNonBindable to stop evaluation.</p>
<p ngNonBindable>This should not evaluate: {{ 1 + 1 }}</p>
<!-- #enddocregion ngNonBindable -->

<!-- #docregion ngNonBindable-with-directive -->
<h3>ngNonBindable with a directive</h3>
<div ngNonBindable [appHighlight]="'yellow'">
  This should not evaluate: {{ 1 + 1 }}, but will highlight yellow.
</div>
<!-- #enddocregion ngNonBindable-with-directive -->

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.