Directives

This section documents modern, standalone Angular — signals, the built-in @if / @for / @switch control flow, @defer, typed reactive forms, provideHttpClient, functional guards and interceptors, and server-side rendering with hydration — as described by the official documentation at angular.dev, which is the reference these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against angular.dev before being relied on in production. Angular ships a major release roughly every six months and its APIs continue to evolve: the examples here target the current major release; where a consulted source disagrees with the current documentation, the documentation wins and the difference is noted.

This section’s bibliography lists the reference material consulted while preparing these pages.

A directive is a class that attaches behaviour to an element. Components are directives with a template; plain directives have none. Angular has two kinds: attribute directives change the appearance or behaviour of an existing element, and structural directives add or remove elements from the DOM. See Directives.

Attribute directives

Declare one with @Directive, give it an attribute selector, and bind to its host element with the host metadata or the host decorators. Signal input() reads a bound value; @HostListener-style entries in host react to events. Directives are standalone by default and are added to a component’s imports.

import { Directive, ElementRef, inject, input } from '@angular/core';

@Directive({
  selector: '[appHighlight]',
  host: {
    '(mouseenter)': 'onEnter()',
    '(mouseleave)': 'onLeave()',
    '[style.transition]': '"background-color 150ms"',
  },
})
export class HighlightDirective {
  private readonly el = inject(ElementRef<HTMLElement>);

  // <p [appHighlight]="'gold'"> -> the directive's own value
  readonly appHighlight = input<string>('yellow');

  private onEnter(): void {
    this.el.nativeElement.style.backgroundColor = this.appHighlight();
  }

  private onLeave(): void {
    this.el.nativeElement.style.backgroundColor = '';
  }
}
<p [appHighlight]="'gold'">Hover me</p>

Prefer binding through host metadata or @HostBinding / @HostListener over touching nativeElement directly, so the directive stays testable and server-side-render safe. See Attribute directives.

Structural directives

A structural directive is written with the * shorthand. Angular desugars * onto an <ng-template> that wraps the host element, and the directive decides whether and how often to render that template.

<!-- what you write -->
<section *appUnless="isLoading">Content</section>

<!-- what Angular expands it to -->
<ng-template [appUnless]="isLoading">
  <section>Content</section>
</ng-template>

<ng-container> is a grouping element that produces no DOM node — useful to host a structural directive without adding a wrapper, or to place several @for / @if blocks side by side. See ng-container and ng-template.

<ng-container *appUnless="isLoading">
  <h2>{{ title() }}</h2>
  <p>No wrapper element is emitted around these two.</p>
</ng-container>

Write one by injecting TemplateRef (the content to stamp out) and ViewContainerRef (where to stamp it).

import { Directive, TemplateRef, ViewContainerRef, effect, inject, input } from '@angular/core';

@Directive({ selector: '[appUnless]' })
export class UnlessDirective {
  private readonly tpl = inject(TemplateRef<unknown>);
  private readonly vcr = inject(ViewContainerRef);

  readonly appUnless = input.required<boolean>();

  constructor() {
    effect(() => {
      this.vcr.clear();
      if (!this.appUnless()) {
        this.vcr.createEmbeddedView(this.tpl);
      }
    });
  }
}

The built-in @if / @for / @switch control flow (see Control flow and @defer) replaces *ngIf / *ngFor / *ngSwitch for everyday branching and lists; custom structural directives remain useful for reusable rendering rules. See Structural directives.

Built-in attribute directives

Import these from @angular/common. With the built-in control flow they are needed less often, but they are still current:

Directive Use

NgClass

Toggle several CSS classes from an object or array: [ngClass]="\{ active: isActive() }". For a single class prefer [class.active]="isActive()".

NgStyle

Set several inline styles from an object: [ngStyle]="\{ color: c() }". For one property prefer [style.color]="c()".

NgModel

Two-way bind a form field in template-driven forms ([(ngModel)]). See Forms.

NgOptimizedImage

Apply the ngSrc attribute to <img> for automatic lazy-loading, srcset generation, and LCP prioritisation (priority).

<button [ngClass]="{ primary: kind() === 'primary', danger: kind() === 'danger' }">Go</button>
<div [ngStyle]="{ 'font-size.px': size(), color: color() }">Sized text</div>

<img ngSrc="/assets/hero.jpg" width="1200" height="600" priority alt="Hero" />
import { NgClass, NgStyle, NgOptimizedImage } from '@angular/common';

@Component({
  selector: 'app-banner',
  imports: [NgClass, NgStyle, NgOptimizedImage],
  templateUrl: './banner.html',
})
export class Banner { /* ... */ }

Directive composition API

hostDirectives lets a component or directive apply other directives to its own host element, optionally re-exposing their inputs and outputs. It composes behaviour without inheritance or wrapper elements.

import { Directive, Component, input } from '@angular/core';

@Directive({
  selector: '[appTooltip]',
  host: { '(mouseenter)': 'show()', '(mouseleave)': 'hide()' },
})
export class Tooltip {
  readonly appTooltip = input<string>('');
  show(): void { /* ... */ }
  hide(): void { /* ... */ }
}

@Component({
  selector: 'app-icon-button',
  hostDirectives: [
    { directive: Tooltip, inputs: ['appTooltip: tooltip'] }, // re-expose as `tooltip`
  ],
  template: '<button><ng-content /></button>',
})
export class IconButton {}
<app-icon-button tooltip="Save">💾</app-icon-button>

Host directives must be standalone, and the list is static (it cannot be built at runtime). See Directive composition API.