Angular Control Flow
Control flow directives (@if, @for, @switch) render branches, lists, and cases in templates and replace the legacy *ngIf/*ngFor/[ngSwitch] for new code.
Control Flow Essentials
@if: Conditional blocks with optionalelse if/else.@for: Loops withtrackfor stable identity and optional@emptyfor empty states.@switch: Selects and renders a matching case.- Preferred for new code in Angular 17+; legacy
*ngIf/*ngFor/[ngSwitch]remain supported.
@if (score > 90) { <p>A</p> } @else if (score > 75) { <p>B</p> } @else { <p>C</p> }
<ul>
@for (it of items; track it.id) { <li>{{ it.label }}</li> } @empty { <li>No items</li> }
</ul>
@switch (status) {
@case ('pending') { <p>Pending</p> }
@case ('done') { <p>Done</p> }
@default { <p>Unknown</p> }
}
Code explained
@if ... @else: Branches to render different blocks based on a condition.@for (...; track ...): Iterates and usestrackto keep stable identities for DOM reuse.@switch/@case/@default: Selects and renders a matching case.
Notes:
- Related: See Templates, Directives, and Lists.
- Use
@forwithtrackfor stable list rendering. - Use
@if ...; elsefor readable branches.
Control Flow Basics
Toggle content with @if ...; else.
Iterate with @for and add a track expression for stable DOM updates.
@if (show) { <p>Visible</p> } @else { <p>Hidden</p> }
@for (item of items; track item) { <li>{{ item }}</li> }
Example
Toggle content with @if, and render lists with @for using a track expression:
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `
<h3>Control Flow</h3>
<button (click)="show.set(!show())">Toggle</button>
<button (click)="items.set([])">Clear</button>
<button (click)="reset()">Reset</button>
@if (show()) {
<p>Visible</p>
} @else {
<p>Hidden</p>
}
<ul>
@for (item of items(); track item) {
<li>{{ item }}</li>
} @empty {
<li>No items</li>
}
</ul>
`
})
export class App {
show = signal(true);
items = signal(['One','Two','Three']);
reset() { this.items.set(['One','Two','Three']); }
}
bootstrapApplication(App);
<app-root></app-root>
Example explained
@if ... @else: Toggles the paragraph based on theshow()signal.@for ... track item: Renders the list from theitems()signal and preserves DOM withtrack.@empty: Displays a fallback when the list is empty.- Signals: Buttons update
showanditems, which re-render the view.
Notes:
- Use track: Prefer tracking by a stable key (e.g.,
track it.id) for lists of objects; for primitives,track itemis fine. - Signals: Read signals with
sig()in templates (e.g.,@if (flag()),@for (x of list())). - Legacy equivalence: In
*ngFor, usetrackByto matchtrackin@for. - Legacy syntax:
*ngIf/*ngFor/[ngSwitch]remain supported; prefer@if/@for/@switchfor new code. - Keep logic in TS: Compute flags and derived arrays in the component; keep templates simple.
- No side effects: Avoid side effects inside control-flow blocks; update state in handlers/services.