Składnia Control Flow w Angular 2026: @if, @for, @switch i pytania rekrutacyjne

Kompletny przewodnik po blokowej składni przepływu sterowania w Angular. Poznaj @if, @for, @switch z praktycznymi przykładami kodu i przygotuj się do rozmów kwalifikacyjnych.

Angular Control Flow Syntax diagram

Składnia control flow w Angular (@if, @for, @switch) zastępuje dyrektywy strukturalne *ngIf, *ngFor i *ngSwitch, które przez lata dominowały w szablonach Angular. Wprowadzona w Angular 17, stabilna od Angular 18 i domyślna od Angular 22, ta blokowa składnia przybliża szablony do standardowych konstrukcji programistycznych, jednocześnie umożliwiając lepsze optymalizacje wydajności.

Dostępna migracja

Uruchom ng generate @angular/core:control-flow aby automatycznie zmigrować istniejące szablony z dyrektyw strukturalnych do nowej składni control flow. Obie składnie mogą współistnieć podczas migracji, ale ngIf, ngFor i ngSwitch są miękko zdeprecjonowane od Angular 19.

Dlaczego Angular zastąpił dyrektywy strukturalne składnią blokową

Dyrektywy strukturalne wymagały importowania CommonModule, używały mikro-składni różniącej się od JavaScript i zmuszały programistów do opakowywania treści w ng-template dla złożonych warunków. Nowa składnia control flow rozwiązuje te problemy:

  • Brak wymaganych importów: @if, @for i @switch są wbudowane we framework
  • Składnia przypominająca JavaScript: warunki i pętle czytają się jak standardowy kod
  • Natywne wsparcie dla @else: koniec z referencjami do ng-template dla gałęzi else
  • Lepszy tree-shaking: nieużywane bloki control flow mają zerowy wpływ na bundle

Dokumentacja Angular zaleca pełne przyjęcie składni blokowej we wszystkich nowych projektach i stopniową migrację istniejących baz kodu.

@if: Renderowanie warunkowe bez ng-template

Blok @if warunkowo renderuje treść na podstawie wyrażenia prawdziwego. W przeciwieństwie do *ngIf, wspiera bezpośrednio gałęzie @else if i @else:

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

Blok @if wspiera również aliasowanie zmiennych za pomocą słowa kluczowego as, które wyodrębnia wartości z zagnieżdżonych wyrażeń:

html
<!-- Unika wielokrotnego dostępu do właściwości -->
@if (user().profile?.settings?.theme; as theme) {
  <p>Aktualny motyw: {{ theme }}</p>
}

Ten wzorzec jest szczególnie przydatny podczas pracy z danymi asynchronicznymi lub głęboko zagnieżdżonymi obiektami, ponieważ aliasowana zmienna jest definiowana tylko gdy warunek jest prawdziwy.

@for: Iteracja z obowiązkowym wyrażeniem track

Blok @for iteruje po dowolnym iterowalnym obiekcie JavaScript, z optymalizacjami dla tablic. W przeciwieństwie do *ngFor, wymaga wyrażenia track:

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) {
      <div class="product-card">
        <h3>{{ product.name }}</h3>
        <p>{{ product.price | currency }}</p>
      </div>
    } @empty {
      <p>Brak dostępnych produktów</p>
    }
  `
})
export class ProductListComponent {
  products = input.required<Product[]>();
}

Wyrażenie track jest obowiązkowe, ponieważ umożliwia algorytmowi różnicowania Angular identyfikację elementów między cyklami detekcji zmian. Dzięki temu framework wie, które elementy DOM należy ponownie użyć, a które utworzyć od nowa.

Dostęp do zmiennych kontekstowych w @for

Blok @for udostępnia kilka zmiennych kontekstowych:

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

Dostępne zmienne kontekstowe to:

  • $index - bieżący indeks (od 0)
  • $first - czy to pierwszy element
  • $last - czy to ostatni element
  • $even - czy indeks jest parzysty
  • $odd - czy indeks jest nieparzysty
  • $count - całkowita liczba elementów w kolekcji

@switch: Rozgałęzienia wielokrotne bez ng-container

Blok @switch obsługuje scenariusze z wieloma warunkami bez potrzeby zagnieżdżania elementów ng-container:

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

type NotificationType = 'success' | 'warning' | 'error' | 'info';

@Component({
  selector: 'app-notification',
  template: `
    @switch (type()) {
      @case ('success') {
        <div class="alert alert-success">
          <span class="icon-check"></span>
          <ng-content />
        </div>
      }
      @case ('warning') {
        <div class="alert alert-warning">
          <span class="icon-warning"></span>
          <ng-content />
        </div>
      }
      @case ('error') {
        <div class="alert alert-error">
          <span class="icon-error"></span>
          <ng-content />
        </div>
      }
      @default {
        <div class="alert alert-info">
          <span class="icon-info"></span>
          <ng-content />
        </div>
      }
    }
  `
})
export class NotificationComponent {
  type = input<NotificationType>('info');
}

Blok @switch używa ścisłego porównania (===), co eliminuje nieoczekiwane zachowanie związane z koercją typów.

Zagnieżdżanie bloków control flow

Bloki control flow można swobodnie zagnieżdżać, tworząc złożoną logikę renderowania:

html
@if (isLoading()) {
  <app-skeleton-loader />
} @else {
  @for (category of categories(); track category.id) {
    <section class="category">
      <h2>{{ category.name }}</h2>
      @for (product of category.products; track product.id) {
        @switch (product.availability) {
          @case ('in_stock') {
            <app-product-card [product]="product" />
          }
          @case ('pre_order') {
            <app-preorder-card [product]="product" />
          }
          @default {
            <app-out-of-stock-card [product]="product" />
          }
        }
      } @empty {
        <p>Ta kategoria nie ma produktów</p>
      }
    </section>
  } @empty {
    <p>Brak kategorii do wyświetlenia</p>
  }
}

Optymalizacje wydajności z nową składnią

Nowa składnia control flow przynosi znaczące ulepszenia wydajności:

Szybszy Change Detection

Bloki @for z wyrażeniem track umożliwiają Angular bardziej efektywne porównywanie kolekcji. Framework może pominąć sprawdzanie elementów, które się nie zmieniły, bazując na tożsamości śledzonej wartości.

Lepsza minifikacja

Ponieważ składnia blokowa nie wymaga importów z CommonModule, nieużywane bloki mogą być całkowicie usunięte podczas budowania produkcyjnego.

Optymalizacja @defer

Składnia control flow współpracuje z @defer dla leniwego ładowania:

html
@defer (on viewport) {
  @for (item of heavyItems(); track item.id) {
    <app-heavy-component [data]="item" />
  }
} @placeholder {
  <div class="placeholder">Ładowanie...</div>
}

Migracja z dyrektyw strukturalnych

Proces migracji jest prosty dzięki wbudowanemu schematowi:

bash
ng generate @angular/core:control-flow

Schematyk automatycznie przekształca:

Stara składniaNowa składnia
*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; trackBy: trackFn"@for (item of items; track trackFn($index, item)) { }
[ngSwitch] + *ngSwitchCase@switch + @case

Gotowy na rozmowy o Angular?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Pytania rekrutacyjne dotyczące Angular Control Flow

Poniżej znajduje się zestaw pytań często zadawanych na rozmowach kwalifikacyjnych dotyczących nowej składni control flow.

Pytanie 1: Dlaczego wyrażenie track jest obowiązkowe w @for?

Odpowiedź: Wyrażenie track jest obowiązkowe, ponieważ zapewnia algorytmowi różnicowania Angular unikalny identyfikator dla każdego elementu w kolekcji. Pozwala to frameworkowi efektywnie określić, które elementy DOM należy ponownie wykorzystać, zaktualizować lub usunąć podczas zmian w kolekcji. Bez trackingu Angular musiałby ponownie renderować całą listę przy każdej zmianie, co znacząco wpływałoby na wydajność.

Pytanie 2: Jak @if obsługuje głęboko zagnieżdżone właściwości opcjonalne?

Odpowiedź: Blok @if wspiera aliasowanie zmiennych za pomocą słowa kluczowego as, które pozwala wyodrębnić wartość z zagnieżdżonego wyrażenia i używać jej w bloku. Składnia to @if (wyrażenie; as alias). Aliasowana zmienna jest dostępna tylko wtedy, gdy wyrażenie jest prawdziwe, co eliminuje potrzebę powtarzania operatorów opcjonalnego łańcuchowania w szablonie.

Pytanie 3: Czym różni się @switch od *ngSwitch?

Odpowiedź: @switch jest wbudowany w framework i nie wymaga importów. Używa natywnej składni blokowej zamiast dyrektyw atrybutowych. Eliminuje potrzebę opakowujących elementów ng-container. Używa ścisłego porównania (===), co zapobiega niespodziewanym dopasowaniom związanym z koercją typów. Każdy @case i @default to niezależny blok, co poprawia czytelność kodu.

Pytanie 4: Jak uzyskać dostęp do indeksu i innych metadanych w @for?

Odpowiedź: Blok @for udostępnia zmienne kontekstowe poprzez składnię let: @for (item of items; track item.id; let i = $index, f = $first). Dostępne zmienne to $index, $first, $last, $even, $odd i $count. Te zmienne mogą być przypisane do lokalnych aliasów i używane w bloku.

Pytanie 5: Czy można używać @for z obiektami Map i Set?

Odpowiedź: Tak, blok @for iteruje po dowolnym obiekcie implementującym protokół iteratora JavaScript, w tym Map, Set i generatorach. Dla Map iteracja zwraca pary [klucz, wartość], które można destrukturyzować w szablonie.

Pytanie 6: Jak działa @empty i kiedy jest wywoływany?

Odpowiedź: Blok @empty jest wyświetlany gdy kolekcja w @for jest pusta (ma zerową długość) lub gdy iterowalny obiekt nie zwraca żadnych elementów. Jest to wbudowany odpowiednik sprawdzania items.length === 0 i renderowania alternatywnej treści. Blok @empty jest opcjonalny.

Pytanie 7: Jakie są korzyści wydajnościowe nowej składni control flow?

Odpowiedź: Nowa składnia oferuje kilka korzyści wydajnościowych: brak narzutu importów (zero dodatkowego kodu dla dyrektyw), lepszy tree-shaking (nieużywane ścieżki kodu są eliminowane), bardziej efektywny change detection dzięki obowiązkowemu trackingowi, oraz możliwość przyszłych optymalizacji kompilatora, ponieważ składnia jest parsowana statycznie zamiast interpretowana w runtime.

Najlepsze praktyki dla składni Control Flow

Podczas implementacji nowej składni control flow warto przestrzegać kilku zasad:

  1. Zawsze używaj unikalnych identyfikatorów w track - preferuj track item.id zamiast track $index dla kolekcji, które mogą się zmieniać
  2. Wykorzystuj @empty dla lepszego UX - zawsze definiuj stan pustej listy dla lepszego doświadczenia użytkownika
  3. Unikaj złożonej logiki w wyrażeniach - przenieś skomplikowane obliczenia do komponentu
  4. Stosuj aliasowanie dla zagnieżdżonych właściwości - używaj as dla głęboko zagnieżdżonych wyrażeń opcjonalnych
  5. Migruj stopniowo - obie składnie mogą współistnieć, więc można migrować plik po pliku

Podsumowanie

Składnia control flow w Angular reprezentuje fundamentalną zmianę w sposobie pisania szablonów. Bloki @if, @for i @switch oferują bardziej intuicyjną składnię inspirowaną JavaScript, eliminują potrzebę importowania CommonModule i zapewniają lepszą wydajność dzięki obowiązkowemu trackingowi i optymalizacjom kompilatora. Dla zespołów rozpoczynających nowe projekty Angular, nowa składnia powinna być domyślnym wyborem. Dla istniejących projektów, automatyczna migracja za pomocą schematu ng generate @angular/core:control-flow umożliwia płynne przejście bez ryzyka regresji.

Wyzwanie dnia

Znajdziesz błąd w Angular?

Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Anthony Fillion-Maillet

Autor:

Anthony Fillion-Maillet

Założyciel SharpSkill

Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.

Zaktualizowano 3 września 2026

Tagi

#angular
#angular-17
#angular-control-flow
#frontend
#typescript

Udostępnij

Powiązane artykuły