Skip to content

New Control Flow

@if (items.length) {
<li @for (item of items; track item.id)>{{ item.name }}</li>
}

Notes

  • Replaces many structural directive use cases with built-ins.

Before (Angular ≤ v16 — structural directives)

Section titled “Before (Angular ≤ v16 — structural directives)”
<!-- *ngIf with else template -->
<div *ngIf="items?.length; else empty">
<ul>
<li *ngFor="let item of items; trackBy: trackById">{{ item.name }}</li>
</ul>
</div>
<ng-template #empty>
<p>No items</p>
</ng-template>
trackById(index: number, item: { id: number }) { return item.id; }
@if (items?.length > 0) {
<ul>
@for (item of items; track item.id) {
<li>{{ item.name }}</li>
}
</ul>
} @else {
<p>No items</p>
}
<div *ngIf="user$ | async as user; else loading">
Hello {{ user.name }}
</div>
<ng-template #loading>Loading…</ng-template>
@if (user$ | async; as user) {
Hello {{ user.name }}
} @else {
Loading…
}
@if (status === 'loading') {
<p>Loading…</p>
} @else if (status === 'error') {
<p>Something went wrong.</p>
} @else {
<p>Ready!</p>
}
  • Always provide a stable key with track in @for (e.g., track item.id) to avoid DOM churn.
  • Don’t mix old structural directives (*ngIf, *ngFor) with new syntax on the same element; wrap or nest instead.
  • The as binding (e.g., @if (expr; as value)) is block-scoped; it isn’t visible outside the @if block.
  • Use parentheses for complex conditions to keep expressions clear and avoid precedence surprises.
  • Avoid non-deterministic expressions (like Math.random()/Date.now()) inside control flow when doing SSR to prevent hydration mismatches.