angular/angular
Control flow blocks
The @if, @for, @switch, @let template syntax that replaces *ngIf, *ngFor, *ngSwitch. Introduced in v17 and now the documented default. Implemented in packages/compiler/src/render3/view/template.ts (parser) and packages/core/src/render3/instructions/control_flow.ts (runtime).
What it looks like
@if (user(); as currentUser) {
<p>Welcome {{ currentUser.name }}</p>
} @else {
<p>Please sign in</p>
} @for (item of items(); track item.id) {
<li>{{ item.title }}</li>
} @empty {
<li>No items</li>
} @switch (status()) { @case ('loading') { <spinner /> } @case ('ready') {
<my-data /> } @default { <error-state /> } } @let displayName = user()?.fullName
?? 'Anonymous';Why it exists
The structural-directive form (*ngIf, *ngFor, *ngSwitch) had limitations:
- Type narrowing was awkward.
*ngIf="user as u"couldn't always carry the type through. *ngFor'strackByrequired a method on the component, not a template-local expression.*ngSwitchrequired*ngSwitchCaseinside, which felt redundant.- Composing structural directives on the same element required
<ng-container>gymnastics.
Block syntax fixes all of these. Type narrowing works through the @if (cond; as x) form. track is a first-class block field. @switch is a single block.
Compiler pipeline
graph LR Source["@if / @for / @switch / @let<br/>in template source"] --> Parser["template.ts parser"] Parser --> AST["IfBlock / ForLoopBlock / SwitchBlock / DeclareLetBlock"] AST --> IR["Render3 view IR<br/>(IfBlockOp, RepeaterOp, ...)"] IR --> Codegen["Output IR<br/>(ɵɵconditional, ɵɵrepeater, ...)"] Codegen --> Runtime["Runtime instructions"]
The parser produces dedicated AST node types in packages/compiler/src/render3/r3_ast.ts:
IfBlock,IfBlockBranch—@if/@else if/@else.ForLoopBlock,ForLoopBlockEmpty—@forand its@emptycompanion.SwitchBlock,SwitchBlockCase—@switch/@case/@default.DeferredBlockand friends — the@deferfamily.DeclareLetBlock—@let.
The view compiler under packages/compiler/src/render3/view/template.ts lowers them to IR.
Runtime instructions
Three instruction families in packages/core/src/render3/instructions/:
ɵɵconditional— for@ifand@switch. Selects which branch's embedded view to instantiate.ɵɵrepeater*— for@for. Reconciles a list of items against existing views using the list-reconciler inpackages/core/src/render3/list_reconciliation.ts.@let— generates a closure that captures the let-binding's value and exposes it to subsequent template expressions. Implemented inpackages/core/src/render3/instructions/let_declaration.ts.
@for's track expression is required (review enforces it); the reconciler uses it to identify items and decide whether to reuse, move, or destroy a view.
Type checking
The TCB generator in compiler-cli's typecheck/ subsystem was extended to model block semantics:
@if-narrowed expressions get a refined type in the matching branch.@forintroduces a loop-local variable with the array element type.@letintroduces a constant whose type comes from the right-hand-side expression.
strictTemplates mode catches misuse (e.g., a @for track referencing a property the item doesn't have).
Migration
packages/core/schematics/migrations/control-flow-migration/ converts existing *ngIf/*ngFor/*ngSwitch usage to the block syntax automatically. It's run via ng update @angular/core --migrate-only=control-flow-migration.
The migration handles edge cases:
- Removing redundant
<ng-container>wrappers. - Preserving structural-directive
elsetemplates as@elsebranches. - Translating
trackBy: methodReftotrack methodRef($index, $item).
Where to start when modifying
- A new block type: extend the parser AST in
packages/compiler/src/render3/r3_ast.ts, the parser intemplate.ts, the IR ops, the codegen, the runtime instruction, and the type-check generator. Six places at once — block work is a coordinated change. - A new field on an existing block (e.g.,
@for'strack): same surface but smaller footprint. - A type-check refinement for an existing block: only the TCB generator and tests.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.