Attribute directives and host bindings

Angular · Advanced · 12 min

Directives package cross-cutting behaviour (tooltips, permissions, autofocus) without wrapper components.

A directive is like a component without a template: it attaches behaviour to an existing element. Attribute directives are selected by an attribute, e.g. selector: '[appHighlight]', and applied with <p appHighlight>.

Inside a directive you can:

  • Accept inputs with input(). Using the selector's name as an input alias lets you write <p [appHighlight]="'yellow'">.
  • Inject the host element with inject(ElementRef) (use sparingly; prefer bindings).
  • Declare host bindings and listeners in the host metadata: host: { '[class.active]': 'active()', '(mouseenter)': 'show()', 'role': 'button' }. This is the recommended replacement for the @HostBinding and @HostListener decorators.
  • Inject services and other directives on the same element.

Components can also use host to style or annotate their own host element.

`hostDirectives` composes directives into a component or directive: hostDirectives: [{ directive: Tooltip, inputs: ['tooltip'] }] applies Tooltip to every instance and exposes its input, letting you build components from reusable behaviours without inheritance.

Structural directives (with *, like the legacy *ngIf) create and destroy views from an <ng-template>; with built-in control flow you rarely need to write one.

Remember to add directives to the imports of the components that use them.

Example

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

@Directive({
  selector: '[appHighlight]',
  host: {
    '[style.backgroundColor]': 'active() ? color() : null',
    '(mouseenter)': 'active.set(true)',
    '(mouseleave)': 'active.set(false)',
  },
})
export class Highlight {
  color = input('yellow', { alias: 'appHighlight' });
  protected active = signal(false);
}

@Directive({ selector: '[appAutofocus]' })
export class Autofocus {
  constructor() { const el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement; queueMicrotask(() => el.focus()); }
}

@Component({
  selector: 'app-demo', imports: [Highlight, Autofocus],
  template: `<p appHighlight>Yellow on hover</p> <p [appHighlight]="'lightblue'">Blue</p> <input appAutofocus />`,
})
export class Demo {}

Remember

  • Attribute directives add behaviour via a selector like [appX]
  • Use host: {...} for host bindings and listeners instead of decorators
  • hostDirectives composes reusable behaviour into components

Watch out for

  • Manipulating the DOM directly through ElementRef when a host binding would do; it breaks SSR and bypasses sanitisation.

Check yourself

  1. How do you let `<p [appHighlight]="'red'">` pass 'red' into a directive's color input?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: angular.dev/guide/directives/attribute-directives