What Are Directives?
Directives are classes that add behavior to elements in Angular templates. There are three types:
| Type | Purpose | Examples |
|---|---|---|
| Component | Directive with a template | @Component() |
| Structural | Change DOM layout (add/remove elements) | @if, @for, *ngIf, *ngFor |
| Attribute | Change appearance or behavior | ngClass, ngStyle, custom directives |
Built-in Control Flow (Angular 17+)
Modern Angular uses built-in control flow syntax directly in templates:
@if / @else if / @else
html
@if (user.role === 'admin') {
<app-admin-panel />
} @else if (user.role === 'editor') {
<app-editor-panel />
} @else {
<app-viewer-panel />
}@for with Track
html
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Stock</th>
</tr>
</thead>
<tbody>
@for (product of products; track product.id; let idx = $index; let isFirst = $first; let isLast = $last) {
<tr [class.highlight]="isFirst">
<td>{{ idx + 1 }}. {{ product.name }}</td>
<td>{{ product.price | currency }}</td>
<td>{{ product.stock }}</td>
</tr>
} @empty {
<tr><td colspan="3">No products found.</td></tr>
}
</tbody>
</table>Available context variables in @for:
| Variable | Type | Description |
|---|---|---|
$index | number | Current index (0-based) |
$first | boolean | True for the first item |
$last | boolean | True for the last item |
$even | boolean | True for even-indexed items |
$odd | boolean | True for odd-indexed items |
$count | number | Total items in the collection |
@switch
html
@switch (order.status) {
@case ('pending') {
<span class="badge bg-yellow">โณ Pending</span>
}
@case ('shipped') {
<span class="badge bg-blue">๐ Shipped</span>
}
@case ('delivered') {
<span class="badge bg-green">โ
Delivered</span>
}
@default {
<span class="badge bg-gray">Unknown</span>
}
}Legacy Structural Directives
These still work but the new control flow is preferred:
*ngIf
html
<div *ngIf="isVisible">Visible content</div>
<!-- With else -->
<div *ngIf="data; else loading">
{{ data | json }}
</div>
<ng-template #loading>
<p>Loading...</p>
</ng-template>*ngFor
html
<li *ngFor="let item of items; let i = index; trackBy: trackById">
{{ i + 1 }}. {{ item.name }}
</li>[ngSwitch]
html
<div [ngSwitch]="color">
<p *ngSwitchCase="'red'">Red selected</p>
<p *ngSwitchCase="'blue'">Blue selected</p>
<p *ngSwitchDefault>Other color</p>
</div>To use legacy directives, import CommonModule or the specific directive.
Attribute Directives
ngClass
Dynamically set CSS classes:
html
<!-- Object syntax -->
<div [ngClass]="{
'active': isActive,
'disabled': isDisabled,
'highlight': isHighlighted
}">
Content
</div>
<!-- Array syntax -->
<div [ngClass]="['card', 'shadow', theme]">Content</div>
<!-- String expression -->
<div [ngClass]="getClasses()">Content</div>typescript
export class AppComponent {
isActive = true;
isDisabled = false;
isHighlighted = true;
theme = 'dark';
getClasses(): string {
return this.isActive ? 'active card' : 'inactive card';
}
}ngStyle
Dynamically set inline styles:
html
<div [ngStyle]="{
'background-color': bgColor,
'font-size.px': fontSize,
'padding.rem': padding
}">
Styled content
</div>
<div [ngStyle]="getStyles()">Dynamic</div>Creating Custom Attribute Directives
Custom directives let you encapsulate reusable behavior.
Highlight Directive
typescript
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]',
standalone: true,
})
export class HighlightDirective {
@Input() appHighlight = 'yellow';
@Input() defaultColor = '';
constructor(private el: ElementRef) {}
@HostListener('mouseenter')
onMouseEnter() {
this.highlight(this.appHighlight || this.defaultColor || 'yellow');
}
@HostListener('mouseleave')
onMouseLeave() {
this.highlight('');
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}Usage:
html
<p appHighlight="lightblue">Hover to highlight blue!</p>
<p [appHighlight]="selectedColor">Dynamic color</p>
<p appHighlight>Default yellow highlight</p>Auto-Focus Directive
typescript
import { Directive, ElementRef, AfterViewInit } from '@angular/core';
@Directive({
selector: '[appAutoFocus]',
standalone: true,
})
export class AutoFocusDirective implements AfterViewInit {
constructor(private el: ElementRef) {}
ngAfterViewInit() {
this.el.nativeElement.focus();
}
}html
<input appAutoFocus placeholder="I'm auto-focused!">Tooltip Directive
typescript
import { Directive, Input, ElementRef, HostListener, Renderer2 } from '@angular/core';
@Directive({
selector: '[appTooltip]',
standalone: true,
})
export class TooltipDirective {
@Input('appTooltip') tooltipText = '';
private tooltipElement: HTMLElement | null = null;
constructor(
private el: ElementRef,
private renderer: Renderer2,
) {}
@HostListener('mouseenter')
show() {
this.tooltipElement = this.renderer.createElement('span');
this.renderer.appendChild(
this.tooltipElement,
this.renderer.createText(this.tooltipText),
);
this.renderer.addClass(this.tooltipElement, 'tooltip');
this.renderer.appendChild(this.el.nativeElement, this.tooltipElement);
}
@HostListener('mouseleave')
hide() {
if (this.tooltipElement) {
this.renderer.removeChild(this.el.nativeElement, this.tooltipElement);
this.tooltipElement = null;
}
}
}html
<button appTooltip="Click to save your changes">Save</button>Creating Custom Structural Directives
typescript
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appRepeat]',
standalone: true,
})
export class RepeatDirective {
constructor(
private templateRef: TemplateRef<unknown>,
private viewContainer: ViewContainerRef,
) {}
@Input() set appRepeat(times: number) {
this.viewContainer.clear();
for (let i = 0; i < times; i++) {
this.viewContainer.createEmbeddedView(this.templateRef, {
$implicit: i,
index: i,
});
}
}
}html
<p *appRepeat="5; let i">Star {{ i + 1 }} โญ</p>Directive vs Component
| Feature | Component | Directive |
|---|---|---|
| Has template | โ Yes | โ No |
| Has selector | โ Yes (element) | โ Yes (attribute) |
| Adds behavior | โ Yes | โ Yes |
| Multiple per element | โ One | โ Many |
| Use case | UI blocks | Behavior/styling |
Next Steps
Now that you understand directives for controlling DOM behavior, let's explore pipes โ Angular's tool for transforming data directly in templates.