Directives
|
This section documents modern, standalone Angular — signals, the built-in 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 |
|---|---|
Toggle several CSS classes from an object or array: |
|
Set several inline styles from an object: |
|
Two-way bind a form field in template-driven forms ( |
|
Apply the |
<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.