Angular Directives
Directives let you attach extra behavior to DOM elements, and Angular splits them into structural directives that reshape the DOM and attribute directives that modify an existing element.
What Directives Do
A directive is a class that runs logic against a host element in the template, without necessarily rendering its own markup the way a component does. In fact, every component is technically a directive with a template attached. Angular ships several built-in directives and lets you write your own.
Structural Directives
Structural directives change the shape of the DOM by adding, removing, or repeating elements entirely. They are traditionally written with a leading asterisk, like *ngIf and *ngFor, which is shorthand Angular expands into an <ng-template>. Modern Angular also offers built-in control flow blocks such as @if and @for that achieve the same result with cleaner syntax and better type-checking.
Structural directive: *ngFor rendering a list
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
@Component({
selector: 'app-task-list',
standalone: true,
imports: [CommonModule],
template: `
<ul>
<li *ngFor="let task of tasks; index as i">
{{ i + 1 }}. {{ task }}
</li>
</ul>
`
})
export class TaskListComponent {
tasks = ['Write report', 'Review PR', 'Deploy build'];
}
bootstrapApplication(TaskListComponent).catch((err) => console.error(err));
Attribute Directives
Attribute directives change the appearance or behavior of an element without adding or removing it from the DOM. NgClass and NgStyle are the most common built-ins, letting you toggle CSS classes or inline styles based on component state.
Attribute directive: NgClass toggling styles
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
@Component({
selector: 'app-status-badge',
standalone: true,
imports: [CommonModule],
template: `
<span [ngClass]="{ active: isActive, inactive: !isActive }">
{{ isActive ? 'Active' : 'Inactive' }}
</span>
`
})
export class StatusBadgeComponent {
isActive = true;
}
bootstrapApplication(StatusBadgeComponent).catch((err) => console.error(err));
Writing a Custom Attribute Directive
You are not limited to the built-ins. A custom directive is just a class decorated with @Directive that injects ElementRef or listens for host events to change how an element behaves.
A custom highlight directive
import { Component, Directive, ElementRef, HostListener } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
@Directive({
selector: '[appHighlight]',
standalone: true
})
export class HighlightDirective {
constructor(private el: ElementRef) {}
@HostListener('mouseenter')
onEnter() {
this.el.nativeElement.style.backgroundColor = 'yellow';
}
@HostListener('mouseleave')
onLeave() {
this.el.nativeElement.style.backgroundColor = '';
}
}
@Component({
selector: 'app-root',
standalone: true,
imports: [HighlightDirective],
template: `
<p appHighlight>Hover over this text to see the highlight directive in action.</p>
`
})
export class AppComponent {}
bootstrapApplication(AppComponent).catch((err) => console.error(err));
- Structural directives reshape the DOM tree; attribute directives only touch the element they sit on.
- Only one structural directive can be applied per host element — combine *ngIf and *ngFor by wrapping one in an <ng-container>.
- Custom directive selectors are usually written in square brackets, like [appHighlight], so they apply as an attribute rather than a new tag.
- The newer @if / @for / @switch control-flow blocks do not require importing CommonModule at all.
Exercise: Angular Directives
What distinguishes structural directives from attribute directives?