@switch — jedna wartość, wiele możliwości
Wydarzenie może mieć jeden z kilku stanów: wolne miejsca, ostatnie miejsca, brak miejsc, odwołane. Łańcuch @else if na cztery piętra robi się nieczytelny. Gdy porównujesz jedną wartość z listą możliwości, wygodniejszy jest @switch.
Czego się dziś nauczysz
- Wybierzesz fragment szablonu na podstawie jednej wartości blokami
@switchi@case. - Obsłużysz nieprzewidziane wartości blokiem
@default. - Porównasz
@switchz łańcuchem@else ifi wybierzesz właściwy. - Użyjesz
@switchwewnątrz pętli@for. - Porównasz zarówno teksty (w apostrofach), jak i liczby.
- Zbudujesz zakładki Bootstrapa sterowane sygnałem, bez JavaScriptu Bootstrapa.
Przygotowanie: lekcje 12–13 — znasz @if, @else if i @for. Przewidywany czas: 45 min z zadaniami.
Jedna wartość, wiele odpowiedzi
Pole status może mieć wartość 'wolne', 'ostatnie', 'brak' albo 'odwolane'. Dla każdej chcesz pokazać inną etykietę. Z @if wyszłoby cztery razy to samo porównanie status() === …. @switch zapisuje to krócej: „spójrz na status i pokaż przypadek, który pasuje”. Znasz to z instrukcji switch w TypeScripcie.
@switch (status()) {
@case ('wolne') { <span>…</span> }
@case ('brak') { <span>…</span> }
@default { <span>żaden przypadek nie pasuje</span> }
}
@END
<div class="sd-scroll">
<table class="sd-table">
<tr><th style="width:22%">Część</th><th>Znaczenie</th></tr>
<tr><td><code>@switch (x)</code></td><td>wartość, którą sprawdzamy — zwykle sygnał albo pole elementu w pętli</td></tr>
<tr><td><code>@case (y)</code></td><td>fragment, gdy <code>x === y</code>. Wielkość liter ma znaczenie, typ też: <code>'1'</code> to nie <code>1</code></td></tr>
<tr><td><code>@default</code></td><td>gdy żaden przypadek nie pasuje; nieobowiązkowy, ale łapie literówki w danych</td></tr>
</table>
</div>
@@BOX info | Bez break
<p>W przeciwieństwie do <code>switch</code> w TypeScripcie nie piszesz tu <code>break</code>. Każdy <code>@case</code> ma własne klamry i Angular nigdy nie „przelatuje” do następnego. Wewnątrz <code>@switch</code> mogą stać <strong>tylko</strong> bloki <code>@case</code> i <code>@default</code>.</p>
Status wydarzenia
Cztery przyciski ustawiają status, a @switch wybiera etykietę w odpowiednim kolorze.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-demo',
imports: [],
templateUrl: './demo.html'
})
export class Demo {
readonly status = signal('wolne');
ustaw(nowy: string): void {
this.status.set(nowy);
}
}
<div class="p-3">
<div class="mb-3">
<button class="btn btn-sm btn-outline-secondary me-1" (click)="ustaw('wolne')">wolne</button>
<button class="btn btn-sm btn-outline-secondary me-1" (click)="ustaw('ostatnie')">ostatnie</button>
<button class="btn btn-sm btn-outline-secondary me-1" (click)="ustaw('brak')">brak</button>
<button class="btn btn-sm btn-outline-secondary" (click)="ustaw('odwolane')">odwołane</button>
</div>
@switch (status()) {
@case ('wolne') {
<span class="badge text-bg-success fs-6">Są wolne miejsca</span>
}
@case ('ostatnie') {
<span class="badge text-bg-warning fs-6">Ostatnie miejsca!</span>
}
@case ('brak') {
<span class="badge text-bg-danger fs-6">Brak miejsc</span>
}
@case ('odwolane') {
<span class="badge text-bg-secondary fs-6">Wydarzenie odwołane</span>
}
}
</div>
[wolne] [ostatnie] [brak] [odwołane]
[Ostatnie miejsca!] (żółta etykieta)
Każdy przycisk pokazuje inną etykietę. Zawsze widać dokładnie jedną.
Zatrzymaj się
Porównaj ten zapis z łańcuchem @if / @else if z lekcji 12. Ile razy trzeba by tam napisać status()?
Nieznana wartość: @default
Dane często przychodzą z zewnątrz — z pliku, z serwera, od innego programisty. Ktoś wpisze 'Wolne' wielką literą albo 'zamkniete', którego nie przewidziałeś. Tu status wpisujesz ręcznie w pole.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-demo',
imports: [],
templateUrl: './demo.html'
})
export class Demo {
readonly status = signal('wolne');
}
<div class="p-3">
<input #pole class="form-control mb-2" style="width: 18rem" placeholder="wpisz status">
<button class="btn btn-primary mb-3" (click)="status.set(pole.value)">Ustaw</button>
<p>
@switch (status()) {
@case ('wolne') { <span class="badge text-bg-success">Są wolne miejsca</span> }
@case ('brak') { <span class="badge text-bg-danger">Brak miejsc</span> }
@default { <span class="badge text-bg-dark">Nieznany status: „{{ status() }}”</span> }
}
</p>
</div>
[Nieznany status: „Wolne”] (ciemna etykieta)
Tylko dokładne wolne i brak trafiają we właściwy przypadek — reszta ląduje w @default, który pokazuje, co przyszło. Zauważ (click)="status.set(pole.value)" — jedno proste wywołanie można zapisać wprost w szablonie; gdy kod robi się dłuższy, przenieś go do metody.
Zatrzymaj się
Jak poprawić program, żeby Wolne i WOLNE też trafiały w przypadek wolne? Wystarczy jedna metoda tekstowa z lekcji 05.
@switch wewnątrz @for
Każde wydarzenie na liście ma własny status, więc każdy wiersz pokazuje inną etykietę. To najczęstsze użycie @switch w prawdziwych aplikacjach.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-demo',
imports: [],
templateUrl: './demo.html'
})
export class Demo {
readonly wydarzenia = signal([
{ id: 1, tytul: 'Turniej retro', status: 'wolne' },
{ id: 2, tytul: 'Noc robotów', status: 'brak' },
{ id: 3, tytul: 'Warsztat druku 3D', status: 'ostatnie' },
{ id: 4, tytul: 'Hackathon', status: 'odwolane' },
]);
}
<div class="p-3">
<ul class="list-group" style="width: 28rem">
@for (w of wydarzenia(); track w.id) {
<li class="list-group-item d-flex justify-content-between">
{{ w.tytul }}
@switch (w.status) {
@case ('wolne') { <span class="badge text-bg-success">wolne</span> }
@case ('ostatnie') { <span class="badge text-bg-warning">ostatnie</span> }
@case ('brak') { <span class="badge text-bg-danger">brak</span> }
@default { <span class="badge text-bg-secondary">odwołane</span> }
}
</li>
}
</ul>
</div>
Turniej retro [wolne]
Noc robotów [brak]
Warsztat druku 3D [ostatnie]
Hackathon [odwołane]
Wewnątrz pętli porównujemy w.status — bez nawiasów, bo w to zwykły obiekt, nie sygnał.
Zatrzymaj się
Status odwolane obsługuje tu @default. Czy to dobry pomysł? Co pokaże lista, jeśli ktoś wpisze w danych 'wolen'?
Zakładki sterowane sygnałem
Zakładki (nav nav-tabs) to klasyczny element stron. Sygnał pamięta, która zakładka jest aktywna; @switch pokazuje odpowiednią treść, a wiązanie [class.active] podświetla kliknięty nagłówek.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-demo',
imports: [],
templateUrl: './demo.html'
})
export class Demo {
readonly zakladka = signal('opis');
}
<div class="p-3" style="width: 32rem">
<ul class="nav nav-tabs">
<li class="nav-item">
<button class="nav-link" [class.active]="zakladka() === 'opis'" (click)="zakladka.set('opis')">Opis</button>
</li>
<li class="nav-item">
<button class="nav-link" [class.active]="zakladka() === 'regulamin'" (click)="zakladka.set('regulamin')">Regulamin</button>
</li>
<li class="nav-item">
<button class="nav-link" [class.active]="zakladka() === 'kontakt'" (click)="zakladka.set('kontakt')">Kontakt</button>
</li>
</ul>
<div class="border border-top-0 p-3">
@switch (zakladka()) {
@case ('opis') { <p>Turniej gier retro dla uczniów wszystkich klas. Sala 12, piątek, 16:00.</p> }
@case ('regulamin') { <p>Drużyny dwuosobowe. Gramy na sprzęcie szkolnym. Obowiązuje fair play.</p> }
@case ('kontakt') { <p>Opiekun koła: sala 12, przerwa po 3. lekcji.</p> }
}
</div>
</div>
Opis | [Regulamin] | Kontakt
Drużyny dwuosobowe. Gramy na sprzęcie szkolnym. Obowiązuje fair play.
Nie potrzebujesz JavaScriptu Bootstrapa — wszystko robi jeden sygnał. border border-top-0 rysuje ramkę pod zakładkami bez górnej krawędzi.
Zatrzymaj się
Nazwa zakładki jest wpisana w szablonie aż dziewięć razy. Wpisz w jednym @case literówkę ('regulamn'). Co się stanie po kliknięciu i czy kompilator to wyłapie?
Zestawienie elementów
| Element | Znaczenie | Uwagi |
|---|---|---|
@switch (wartość) { … } | wybór jednego przypadku | Bez importu. |
@case ('tekst'), @case (3) | przypadek przy równości === | Tekst w apostrofach, liczba bez. |
@default { … } | wszystko inne | Najwyżej jeden. |
nav nav-tabs, nav-item, nav-link | zakładki Bootstrapa | active — podświetlona. |
border, border-top-0 | ramka, ramka bez góry | Bootstrap. |
btn-sm, me-1 | mały przycisk, mały odstęp | Z lekcji 03. |
Zanim utkniesz
| Zapis | Problem |
|---|---|
@case 'wolne' { … } | NG5002 — wartość w @case musi być w nawiasach okrągłych. |
@case (wolne) bez apostrofów | Angular szuka pola wolne w klasie i zgłasza błąd, że go nie ma. Teksty zawsze w apostrofach. |
@case ('Wolne'), a w danych 'wolne' | Nic się nie pokazuje (albo @default). Porównanie uwzględnia wielkość liter. |
@case ('1') przy sygnale z liczbą | Błąd typu — tekst nie jest porównywalny z liczbą. Liczby bez apostrofów: @case (1). |
Treść między @switch a pierwszym @case | NG5002: @switch block can only contain @case and @default blocks. |
Dwa bloki @default | NG5002: @switch block can only have one @default block. |
Zadania
Sygnał przechowuje numer dnia od 1 do 7. Przyciski „poprzedni” i „następny” zmieniają go w kółko (po 7 jest 1, przed 1 jest 7). @switch pokazuje nazwę dnia.
Kryterium sukcesu: wszystkie siedem nazw się wyświetla, a przejścia 7 → 1 i 1 → 7 działają.
Podpowiedź
Porównujesz liczby, więc @case (1) — bez apostrofów. „W kółko” zrobisz warunkiem w update.
Cztery przyciski: „słońce”, „deszcz”, „śnieg”, „burza” ustawiają sygnał pogoda. @switch pokazuje duże emoji (☀️ 🌧️ ❄️ ⛈️) i jedno zdanie porady, np. „Weź parasol”.
Kryterium sukcesu: każdy przycisk zmienia emoji i poradę, zawsze widać jedną parę.
Podpowiedź
Emoji powiększysz klasą display-1.
Tablica zamówień { id, produkt, status }, gdzie status to 'nowe', 'w realizacji', 'gotowe' albo 'odebrane'. Pokaż listę z etykietą w innym kolorze dla każdego statusu oraz przyciskiem „Dalej”, który przesuwa status zamówienia do następnego etapu (przy „odebrane” przycisk znika).
Kryterium sukcesu: etykiety mają cztery różne kolory, „Dalej” zmienia status tylko jednego zamówienia, a przy ostatnim etapie przycisku nie ma.
Podpowiedź
Metoda dalej(id) robi update z map; następny status wybierz w metodzie pomocniczej ze switch w TypeScripcie.
Dwa pola tekstowe (#a, #b) i cztery przyciski: + − × ÷. Kliknięcie zapisuje liczby i wybrane działanie w sygnałach. @switch pokazuje wynik odpowiedniego działania; przy dzieleniu przez zero — komunikat o błędzie zamiast Infinity.
Kryterium sukcesu: każde działanie daje poprawny wynik, a dzielenie przez zero pokazuje komunikat.
Podpowiedź
Liczby z pól: Number(a.value). W przypadku dzielenia użyj @if wewnątrz @case ('/').
Rozbuduj przykład 4 o czwartą zakładkę „Zapisani”, która pokazuje listę uczestników (sygnał z tablicą, @for z @empty) i pole z przyciskiem do dopisania osoby. Na nagłówku zakładki pokaż liczbę zapisanych w odznace, np. „Zapisani [3]”.
Kryterium sukcesu: przełączanie zakładek działa, dopisanie osoby zwiększa liczbę w nagłówku, a pusta lista pokazuje komunikat.
Podpowiedź
Zawartość jednego @case może być dowolnie długa — także z @for. Odznaka: <span class="badge text-bg-secondary">{{ osoby().length }}</span>.
Zrób sygnalizację świetlną: fazy 'czerwone', 'czerwone-zolte', 'zielone', 'zolte'. Przycisk „Dalej” przechodzi do następnej fazy w kółko. Narysuj trzy koła (div z klasą rounded-circle, szerokość i wysokość 60 px) i zapal właściwe — zgaszone są szare. Pod sygnalizatorem @switch pokazuje komunikat dla kierowcy („Stój”, „Przygotuj się”, „Jedź”, „Zwolnij”).
Kryterium sukcesu: kolejność faz zgadza się z prawdziwą sygnalizacją, po żółtym wraca czerwone, a komunikat pasuje do świateł.
Podpowiedź
Kolejność faz zapisz w tablicy, a w sygnale trzymaj numer fazy; nazwę fazy daje computed. Koła zapalasz wiązaniem [style.background] z operatorem ?:.
Co trzeba zapamiętać
@switch (wartość) { @case (x) { … } }pokazuje przypadek, w którym wartość jest dokładnie równax.@defaultłapie wszystko, czego nie przewidziałeś — także literówki w danych.- Nie ma
breaki nie ma „przelatywania” do następnego przypadku. - Teksty w
@casepiszesz w apostrofach, liczby bez. @switchsprawdza się przy statusach, zakładkach i wszędzie tam, gdzie jedna wartość ma kilka odpowiedzi; progi liczbowe („co najmniej”) zostają przy@if.
Sprawdź się: kiedy wybierzesz @switch, a kiedy łańcuch @if/@else if? Podaj po jednym przykładzie z lekcji 12 i 14.
Dokumentacja: angular.dev — blok @switch.