Angular Control Flow Syntax in 2026: @if, @for, @switch en Interviewvragen

Uitgebreide gids over de nieuwe Angular Control Flow Syntax met @if, @for en @switch. Performance-optimalisaties, Signal-integratie en veelgestelde interviewvragen voor Angular-ontwikkelaars.

Angular Control Flow Syntax diagram met @if, @for en @switch blocks

De Control Flow Syntax van Angular (@if, @for, @switch) vervangt de structurele directives *ngIf, *ngFor en *ngSwitch die jarenlang Angular-templates domineerden. Geïntroduceerd in Angular 17, stabiel sinds Angular 18 en standaard in Angular 22, brengt deze block-gebaseerde syntax templates dichter bij standaard programmeerconstructies en maakt betere performance-optimalisaties mogelijk.

Migratie beschikbaar

Voer ng generate @angular/core:control-flow uit om bestaande templates automatisch te migreren van structurele directives naar de nieuwe Control Flow Syntax. Beide syntaxen bestaan naast elkaar tijdens de migratie, maar ngIf, ngFor en ngSwitch zijn soft-deprecated sinds Angular 19.

Waarom Angular Structurele Directives Verving door Block-Syntax

Structurele directives vereisten het importeren van CommonModule, gebruikten microsyntax die verschilde van JavaScript, en dwongen ontwikkelaars om content in ng-template te wrappen voor complexe condities. De nieuwe Control Flow Syntax lost deze problemen op:

  • Geen imports nodig: @if, @for en @switch zijn ingebouwd in het framework
  • JavaScript-achtige syntax: condities en loops lezen als standaardcode
  • Native @else-ondersteuning: geen ng-template-referenties meer nodig voor else-takken
  • Betere tree-shaking: ongebruikte Control Flow Blocks hebben geen impact op de bundlegrootte

De Angular-documentatie raadt aan om block-syntax volledig te adopteren in alle nieuwe projecten en bestaande codebases incrementeel te migreren.

@if: Conditionele Rendering zonder ng-template

Het @if-block rendert conditioneel content gebaseerd op een truthy expressie. In tegenstelling tot *ngIf ondersteunt het direct @else if en @else-takken:

user-status.component.tstypescript
import { Component, input } from '@angular/core';

@Component({
  selector: 'app-user-status',
  template: `
    @if (user().role === 'admin') {
      <app-admin-panel />
    } @else if (user().role === 'editor') {
      <app-editor-panel />
    } @else {
      <app-viewer-panel />
    }
  `
})
export class UserStatusComponent {
  user = input.required<{ role: string }>();
}

Het @if-block ondersteunt ook variabele-aliasing met het as-keyword, dat waarden extraheert uit geneste expressies:

html
<!-- Vermijdt herhaalde property-toegang -->
@if (user().profile?.settings?.theme; as theme) {
  <p>Huidig thema: {{ theme }}</p>
}

Dit patroon is bijzonder nuttig bij het werken met async data of diep geneste objecten, aangezien de gealiaste variabele alleen gedefinieerd is wanneer de conditie truthy is.

@for: Iteratie met Verplichte Track-Expressie

Het @for-block itereert over elk JavaScript-iterable, met optimalisaties voor arrays. In tegenstelling tot *ngFor vereist het een track-expressie:

product-list.component.tstypescript
import { Component, input } from '@angular/core';

interface Product {
  id: string;
  name: string;
  price: number;
}

@Component({
  selector: 'app-product-list',
  template: `
    @for (product of products(); track product.id) {
      <app-product-card [product]="product" />
    } @empty {
      <p>Geen producten beschikbaar</p>
    }
  `
})
export class ProductListComponent {
  products = input.required<Product[]>();
}

De track-expressie vertelt Angular hoe elk item te identificeren over re-renders heen. Het kiezen van de juiste tracking-property heeft directe impact op de performance:

Track-ExpressieToepassingPerformance-Impact
track item.idItems met unieke identifiersOptimaal: minimale DOM-updates
track $indexStatische lijsten die nooit worden gesorteerdAcceptabel: volledige re-render bij herschikking
track itemReferentie-trackingSlecht: nieuwe referentie = nieuwe DOM-node

Contextvariabelen in @for-Blocks

Angular biedt impliciete variabelen binnen @for-blocks die iteratie-metadata exponeren:

html
@for (item of items(); track item.id; let i = $index, isLast = $last) {
  <li class="item" [class.last]="isLast">
    {{ i + 1 }}. {{ item.name }}
  </li>
}

Beschikbare contextvariabelen:

  • $index: nulgebaseerde positie
  • $count: totaal aantal items
  • $first, $last: boolean flags voor eerste/laatste items
  • $even, $odd: boolean flags gebaseerd op index-pariteit

Klaar om je Angular gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

@switch: Type-Safe Conditionele Vertakking

Het @switch-block biedt exhaustieve pattern matching met strikte gelijkheid (===) vergelijking:

status-badge.component.tstypescript
import { Component, input } from '@angular/core';

type OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered';

@Component({
  selector: 'app-status-badge',
  template: `
    @switch (status()) {
      @case ('pending') {
        <span class="badge badge-gray">In Afwachting</span>
      }
      @case ('processing') {
        <span class="badge badge-blue">In Verwerking</span>
      }
      @case ('shipped') {
        <span class="badge badge-yellow">Verzonden</span>
      }
      @case ('delivered') {
        <span class="badge badge-green">Afgeleverd</span>
      }
    }
  `
})
export class StatusBadgeComponent {
  status = input.required<OrderStatus>();
}

In tegenstelling tot JavaScript's switch heeft Angular's @switch geen fallthrough-gedrag. Meerdere condities die op hetzelfde block gericht zijn vereisen opeenvolgende @case-statements:

html
@switch (userRole()) {
  @case ('admin')
  @case ('superadmin') {
    <app-admin-dashboard />
  }
  @case ('editor')
  @case ('reviewer') {
    <app-editor-dashboard />
  }
  @default {
    <app-viewer-dashboard />
  }
}

Exhaustieve Type-Controle met @default never

Angular 22 ondersteunt compile-time exhaustiveness checking. Het gebruik van @default never; declareert dat er geen resterende cases zouden moeten bestaan:

typescript
type Theme = 'light' | 'dark' | 'system';

// Compilatiefout als een Theme-waarde ontbreekt in @case-blocks
@switch (theme()) {
  @case ('light') { /* ... */ }
  @case ('dark') { /* ... */ }
  @case ('system') { /* ... */ }
  @default never;
}

Het toevoegen van een nieuwe waarde aan de Theme-union veroorzaakt een compilatiefout, wat ontwikkelaars dwingt alle cases af te handelen.

Control Flow Syntax en Angular Signals Integratie

Control Flow Blocks integreren naadloos met Angular Signals, waardoor fijnkorrelige reactiviteit mogelijk wordt:

dashboard.component.tstypescript
import { Component, computed, signal } from '@angular/core';

@Component({
  selector: 'app-dashboard',
  template: `
    @if (isLoading()) {
      <app-skeleton />
    } @else if (hasError()) {
      <app-error [message]="errorMessage()" />
    } @else {
      @for (item of filteredItems(); track item.id) {
        <app-item-card [item]="item" />
      } @empty {
        <p>Geen items komen overeen met de filters</p>
      }
    }
  `
})
export class DashboardComponent {
  items = signal<Item[]>([]);
  filter = signal('');
  isLoading = signal(true);
  hasError = signal(false);
  errorMessage = signal('');

  // Computed signal wordt alleen opnieuw berekend wanneer dependencies veranderen
  filteredItems = computed(() =>
    this.items().filter(item =>
      item.name.toLowerCase().includes(this.filter().toLowerCase())
    )
  );
}

Wanneer een signal wordt bijgewerkt, herevalueert Angular alleen de betreffende Control Flow Blocks. Gecombineerd met Angular's zoneless change detection maakt dit hooggeoptimaliseerde rendering mogelijk.

Veelgestelde Interviewvragen over Angular Control Flow

Technische interviews testen vaak het begrip van Control Flow Syntax, vooral de verschillen met structurele directives.

Vraag 1: Waarom is track verplicht in @for?

De track-expressie geeft Angular een stabiele identiteit voor elk item. Zonder deze kan Angular niet efficiënt bepalen welke DOM-nodes moeten worden aangemaakt, bijgewerkt of verwijderd wanneer de collectie verandert. Het verplicht maken van track dwingt ontwikkelaars om een expliciete beslissing over identiteit te nemen, waardoor de performance-valkuilen van impliciete referentie-tracking worden vermeden.

Vraag 2: Hoe verschilt @if van *ngIf met async pipe?

Beide kunnen observables verwerken, maar @if met signals biedt synchrone toegang tot waarden:

typescript
// Met *ngIf en async pipe (legacy)
<div *ngIf="user$ | async as user">{{ user.name }}</div>

// Met @if en signals (modern)
@if (user(); as user) {
  <div>{{ user.name }}</div>
}

De signal-aanpak vermijdt subscription-beheer en integreert beter met Angular's change detection.

Vraag 3: Kan @switch complexe @if/@else if-ketens vervangen?

@switch moet worden gebruikt bij het vergelijken van een enkele expressie met meerdere discrete waarden. Voor complexe boolean-condities die verschillende expressies bevatten, blijft @if/@else if geschikter:

html
<!-- Gebruik @switch voor enkele expressie, meerdere waarden -->
@switch (status()) {
  @case ('active') { ... }
  @case ('inactive') { ... }
}

<!-- Gebruik @if voor meerdere expressies -->
@if (isAdmin() && hasPermission('write')) {
  ...
} @else if (isEditor()) {
  ...
}

Vraag 4: Wat gebeurt er met @for wanneer de collectie leeg is?

Wanneer de iterable geen items bevat, slaat Angular de @for-content volledig over. Het optionele @empty-block rendert fallback-content in dit geval. Zonder @empty wordt er niets gerenderd, wat verschilt van sommige frameworks die een lege container renderen.

Migreren van Structurele Directives naar Control Flow

De Angular CLI biedt een geautomatiseerde migratie-schematic:

bash
# Migreer het hele project
ng generate @angular/core:control-flow

# Migreer een specifieke directory
ng generate @angular/core:control-flow --path=src/app/features

De schematic handelt de meeste transformaties automatisch af, maar controleer de output op edge cases:

Structurele DirectiveControl Flow Equivalent
*ngIf="condition"@if (condition) { }
*ngIf="condition; else elseBlock"@if (condition) { } @else { }
*ngFor="let item of items"@for (item of items; track item) { }
*ngFor="let item of items; index as i"@for (item of items; track item; let i = $index) { }
[ngSwitch] + *ngSwitchCase@switch + @case

Na de migratie, verwijder CommonModule-imports uit standalone components die geen structurele directives meer gebruiken.

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Belangrijke Inzichten over Angular Control Flow Syntax

  • Angular's Control Flow Syntax (@if, @for, @switch) is de aanbevolen aanpak sinds Angular 19, ter vervanging van structurele directives
  • De track-expressie in @for is verplicht: gebruik unieke identifiers zoals item.id voor optimale performance, vermijd track item dat referentie-gebaseerde re-renders veroorzaakt
  • @if ondersteunt native @else if en @else-takken zonder ng-template, en het as-keyword maakt aliassen voor truthy waarden voor hergebruik
  • @switch gebruikt strikte gelijkheid en heeft geen fallthrough: opeenvolgende @case-statements richten zich op hetzelfde block, en @default never; maakt exhaustieve type-controle mogelijk
  • Control Flow Blocks integreren met Signals voor fijnkorrelige reactiviteit: alleen betreffende blocks worden opnieuw gerenderd wanneer signals worden bijgewerkt
  • Voer ng generate @angular/core:control-flow uit om bestaande templates automatisch te migreren
Dagelijkse challenge

Zie jij de bug in Angular?

Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Anthony Fillion-Maillet

Geschreven door

Anthony Fillion-Maillet

Oprichter van SharpSkill

Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.

Bijgewerkt op 3 september 2026

Tags

#angular
#control-flow
#typescript
#template-syntax
#signals

Delen

Gerelateerde artikelen