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 withtrack
for stable identity and optional@empty
for 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 usestrack
to keep stable identities for DOM reuse.@switch/@case/@default
: Selects and renders a matching case.
Notes:
- Related: See Templates, Directives, and Lists.
- Use
@for
withtrack
for stable list rendering. - Use
@if ...; else
for 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
show
anditems
, 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 item
is fine. - Signals: Read signals with
sig()
in templates (e.g.,@if (flag())
,@for (x of list())
). - Legacy equivalence: In
*ngFor
, usetrackBy
to matchtrack
in@for
. - Legacy syntax:
*ngIf
/*ngFor
/[ngSwitch]
remain supported; prefer@if
/@for
/@switch
for 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.