Angular Control Flow Syntax 2026: @if, @for, @switch und Interviewfragen

Umfassende Anleitung zur neuen Angular Control Flow Syntax mit @if, @for und @switch. Performance-Optimierungen, Signal-Integration und häufige Interviewfragen für Angular-Entwickler.

Angular Control Flow Syntax Diagramm mit @if, @for und @switch Blöcken

Die Control Flow Syntax von Angular (@if, @for, @switch) ersetzt die strukturellen Direktiven *ngIf, *ngFor und *ngSwitch, die jahrelang Angular-Templates dominierten. Eingeführt in Angular 17, stabil seit Angular 18 und Standard in Angular 22, bringt diese block-basierte Syntax Templates näher an Standard-Programmierkonstrukte und ermöglicht bessere Performance-Optimierungen.

Migration verfügbar

Mit ng generate @angular/core:control-flow lassen sich bestehende Templates automatisch von strukturellen Direktiven zur neuen Control Flow Syntax migrieren. Beide Syntaxen koexistieren während der Migration, aber ngIf, ngFor und ngSwitch sind seit Angular 19 soft-deprecated.

Warum Angular strukturelle Direktiven durch Block-Syntax ersetzt hat

Strukturelle Direktiven erforderten den Import von CommonModule, verwendeten Microsyntax, die sich von JavaScript unterschied, und zwangen Entwickler dazu, Inhalte für komplexe Bedingungen in ng-template zu wrappen. Die neue Control Flow Syntax löst diese Probleme:

  • Keine Imports erforderlich: @if, @for und @switch sind ins Framework integriert
  • JavaScript-ähnliche Syntax: Bedingungen und Schleifen lesen sich wie Standardcode
  • Native @else-Unterstützung: keine ng-template-Referenzen mehr für else-Zweige
  • Besseres Tree-Shaking: ungenutzte Control Flow Blocks haben keinen Einfluss auf die Bundle-Größe

Die Angular-Dokumentation empfiehlt, die Block-Syntax in allen neuen Projekten vollständig zu übernehmen und bestehende Codebasen schrittweise zu migrieren.

@if: Bedingtes Rendering ohne ng-template

Der @if-Block rendert Inhalte bedingt basierend auf einem truthy Ausdruck. Im Gegensatz zu *ngIf unterstützt er @else if und @else-Zweige direkt:

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 }>();
}

Der @if-Block unterstützt auch Variablen-Aliasing mit dem as-Schlüsselwort, das Werte aus verschachtelten Ausdrücken extrahiert:

html
<!-- Vermeidet wiederholten Property-Zugriff -->
@if (user().profile?.settings?.theme; as theme) {
  <p>Aktuelles Theme: {{ theme }}</p>
}

Dieses Muster ist besonders nützlich bei der Arbeit mit async Daten oder tief verschachtelten Objekten, da die aliasierte Variable nur definiert ist, wenn die Bedingung truthy ist.

@for: Iteration mit obligatorischem Track-Ausdruck

Der @for-Block iteriert über jedes JavaScript-Iterable mit Optimierungen für Arrays. Im Gegensatz zu *ngFor erfordert er einen track-Ausdruck:

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>Keine Produkte verfügbar</p>
    }
  `
})
export class ProductListComponent {
  products = input.required<Product[]>();
}

Der track-Ausdruck teilt Angular mit, wie jedes Element über Re-Renders hinweg identifiziert werden soll. Die Wahl der richtigen Tracking-Property hat direkten Einfluss auf die Performance:

Track-AusdruckAnwendungsfallPerformance-Auswirkung
track item.idElemente mit eindeutigen IdentifiernOptimal: minimale DOM-Updates
track $indexStatische Listen, die nie umsortiert werdenAkzeptabel: vollständiges Re-Render bei Umsortierung
track itemReferenz-TrackingSchlecht: neue Referenz = neuer DOM-Knoten

Kontextvariablen in @for-Blöcken

Angular stellt implizite Variablen innerhalb von @for-Blöcken bereit, die Iterations-Metadaten exponieren:

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

Verfügbare Kontextvariablen:

  • $index: nullbasierte Position
  • $count: Gesamtanzahl der Elemente
  • $first, $last: Boolean-Flags für erstes/letztes Element
  • $even, $odd: Boolean-Flags basierend auf Index-Parität

Bereit für deine Angular-Interviews?

Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.

@switch: Typsichere bedingte Verzweigung

Der @switch-Block bietet erschöpfendes Pattern Matching mit striktem Gleichheitsvergleich (===):

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">Ausstehend</span>
      }
      @case ('processing') {
        <span class="badge badge-blue">In Bearbeitung</span>
      }
      @case ('shipped') {
        <span class="badge badge-yellow">Versendet</span>
      }
      @case ('delivered') {
        <span class="badge badge-green">Zugestellt</span>
      }
    }
  `
})
export class StatusBadgeComponent {
  status = input.required<OrderStatus>();
}

Im Gegensatz zu JavaScripts switch hat Angulars @switch kein Fallthrough-Verhalten. Mehrere Bedingungen, die denselben Block ansteuern, erfordern aufeinanderfolgende @case-Anweisungen:

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

Erschöpfende Typprüfung mit @default never

Angular 22 unterstützt Compile-Time Exhaustiveness Checking. Die Verwendung von @default never; deklariert, dass keine verbleibenden Fälle existieren sollten:

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

// Kompilierfehler, wenn ein Theme-Wert in den @case-Blöcken fehlt
@switch (theme()) {
  @case ('light') { /* ... */ }
  @case ('dark') { /* ... */ }
  @case ('system') { /* ... */ }
  @default never;
}

Das Hinzufügen eines neuen Wertes zur Theme-Union löst einen Kompilierfehler aus und zwingt Entwickler, alle Fälle zu behandeln.

Control Flow Syntax und Angular Signals Integration

Control Flow Blocks integrieren sich nahtlos mit Angular Signals und ermöglichen feinkörnige Reaktivität:

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>Keine Elemente entsprechen Ihren Filtern</p>
      }
    }
  `
})
export class DashboardComponent {
  items = signal<Item[]>([]);
  filter = signal('');
  isLoading = signal(true);
  hasError = signal(false);
  errorMessage = signal('');

  // Computed Signal wird nur neu berechnet, wenn sich Abhängigkeiten ändern
  filteredItems = computed(() =>
    this.items().filter(item =>
      item.name.toLowerCase().includes(this.filter().toLowerCase())
    )
  );
}

Wenn sich ein Signal aktualisiert, evaluiert Angular nur die betroffenen Control Flow Blocks neu. Kombiniert mit Angulars zoneless Change Detection ermöglicht dies hochoptimiertes Rendering.

Häufige Interviewfragen zur Angular Control Flow Syntax

Technische Interviews testen häufig das Verständnis der Control Flow Syntax, insbesondere die Unterschiede zu strukturellen Direktiven.

Frage 1: Warum ist track in @for obligatorisch?

Der track-Ausdruck gibt Angular eine stabile Identität für jedes Element. Ohne ihn kann Angular nicht effizient bestimmen, welche DOM-Knoten erstellt, aktualisiert oder zerstört werden sollen, wenn sich die Collection ändert. Die Pflicht von track zwingt Entwickler zu einer expliziten Entscheidung über Identität und vermeidet die Performance-Fallen von implizitem Referenz-Tracking.

Frage 2: Wie unterscheidet sich @if von *ngIf mit async pipe?

Beide können Observables verarbeiten, aber @if mit Signals bietet synchronen Zugriff auf Werte:

typescript
// Mit *ngIf und async pipe (Legacy)
<div *ngIf="user$ | async as user">{{ user.name }}</div>

// Mit @if und Signals (modern)
@if (user(); as user) {
  <div>{{ user.name }}</div>
}

Der Signal-Ansatz vermeidet Subscription-Management und integriert sich besser mit Angulars Change Detection.

Frage 3: Kann @switch komplexe @if/@else if-Ketten ersetzen?

@switch sollte verwendet werden, wenn ein einzelner Ausdruck mit mehreren diskreten Werten verglichen wird. Für komplexe Boolean-Bedingungen, die verschiedene Ausdrücke involvieren, bleibt @if/@else if angemessener:

html
<!-- Verwende @switch für einzelnen Ausdruck, mehrere Werte -->
@switch (status()) {
  @case ('active') { ... }
  @case ('inactive') { ... }
}

<!-- Verwende @if für mehrere Ausdrücke -->
@if (isAdmin() && hasPermission('write')) {
  ...
} @else if (isEditor()) {
  ...
}

Frage 4: Was passiert mit @for, wenn die Collection leer ist?

Wenn das Iterable keine Elemente enthält, überspringt Angular den @for-Inhalt vollständig. Der optionale @empty-Block rendert Fallback-Inhalt in diesem Fall. Ohne @empty wird nichts gerendert, was sich von einigen Frameworks unterscheidet, die einen leeren Container rendern.

Migration von strukturellen Direktiven zu Control Flow

Die Angular CLI bietet ein automatisiertes Migrations-Schematic:

bash
# Gesamtes Projekt migrieren
ng generate @angular/core:control-flow

# Bestimmtes Verzeichnis migrieren
ng generate @angular/core:control-flow --path=src/app/features

Das Schematic handhabt die meisten Transformationen automatisch, aber überprüfen Sie die Ausgabe auf Grenzfälle:

Strukturelle DirektiveControl Flow Äquivalent
*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

Nach der Migration entfernen Sie CommonModule-Imports aus Standalone-Komponenten, die keine strukturellen Direktiven mehr verwenden.

Fang an zu üben!

Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.

Wichtige Erkenntnisse zur Angular Control Flow Syntax

  • Angulars Control Flow Syntax (@if, @for, @switch) ist seit Angular 19 der empfohlene Ansatz und ersetzt strukturelle Direktiven
  • Der track-Ausdruck in @for ist obligatorisch: Verwenden Sie eindeutige Identifier wie item.id für optimale Performance, vermeiden Sie track item, das referenzbasierte Re-Renders verursacht
  • @if unterstützt native @else if und @else-Zweige ohne ng-template, und das as-Schlüsselwort aliasiert truthy Werte zur Wiederverwendung
  • @switch verwendet strikte Gleichheit und hat kein Fallthrough: aufeinanderfolgende @case-Anweisungen zielen auf denselben Block, und @default never; ermöglicht erschöpfende Typprüfung
  • Control Flow Blocks integrieren sich mit Signals für feinkörnige Reaktivität: nur betroffene Blöcke werden neu gerendert, wenn sich Signals aktualisieren
  • Führen Sie ng generate @angular/core:control-flow aus, um bestehende Templates automatisch zu migrieren
Tägliche Challenge

Findest du den Bug in Angular?

Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Anthony Fillion-Maillet

Geschrieben von

Anthony Fillion-Maillet

Gründer von SharpSkill

Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.

Aktualisiert am 3. September 2026

Tags

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

Teilen

Verwandte Artikel