Dyrektywa @for – pętla w szablonie
@for pozwala wyświetlić całą listę elementów z tablicy – bez kopiowania
kodu HTML dla każdego elementu. Jeden szablon, dowolna liczba elementów.
Poznasz też słowo kluczowe track i zmienne pomocnicze pętli.
Czym jest @for?
Wyobraź sobie, że masz tablicę 20 uczniów i chcesz wyświetlić każdego
w osobnym wierszu tabeli. Bez @for musiałbyś skopiować
ten sam kod HTML 20 razy. Z @for piszesz go raz.
❌ Bez @for – kopiowanie kodu
<!-- 20 razy ten sam kod – koszmar! --> <li>{{ uczniowie[0] }}</li> <li>{{ uczniowie[1] }}</li> <li>{{ uczniowie[2] }}</li> <!-- ... i tak 20 razy -->
✅ Z @for – jeden szablon dla wszystkich
<!-- Jeden wzorzec – Angular powtórzy go tyle razy ile trzeba --> @for (uczen of uczniowie; track uczen) { <li>{{ uczen }}</li> }
@for to odpowiednik pętli for...of z TypeScript –
przebiega przez każdy element tablicy i dla każdego z nich renderuje
podany blok HTML.
Podstawowa składnia
Składnia @for wygląda tak:
@for (element of tablica; track identyfikator) { ... }.
Trzy obowiązkowe części: zmienna iterująca, tablica i słowo kluczowe
track.
export class AppComponent { owoce: string[] = ['Jabłko', 'Banan', 'Mango', 'Kiwi']; }
<ul class="list-group"> @for (owoc of owoce; track owoc) { <li class="list-group-item">{{ owoc }}</li> } </ul> <!-- Wynik: • Jabłko • Banan • Mango • Kiwi -->
Nazwa zmiennej iterującej (owoc) jest dowolna – wybierasz ją sam.
Zwyczajowo używamy liczby pojedynczej gdy tablica ma nazwę w liczbie mnogiej:
owoce → owoc,
uczniowie → uczen,
produkty → produkt.
Zmienna ta jest dostępna tylko wewnątrz bloku { }.
Słowo kluczowe track – dlaczego jest obowiązkowe?
track to obowiązkowa część @for w Angular 17+.
Mówi Angularowi czego używać do identyfikowania każdego elementu listy.
Bez tego Angular nie wie które elementy się zmieniły, a które są nowe.
Wyobraź sobie listę zakupów. Dopisujesz nowy produkt na końcu.
Angular bez track musiałby zniszczyć i odtworzyć
całą listę od nowa. Z track wie dokładnie
który element jest nowy i aktualizuje tylko jego – reszta zostaje nieruszona.
track dla prostych tablic (stringi, liczby)
<!-- Tablica stringów – trackujemy samą wartość --> @for (miasto of miasta; track miasto) { <p>{{ miasto }}</p> } <!-- Tablica liczb – tak samo --> @for (ocena of oceny; track ocena) { <span class="badge bg-primary">{{ ocena }}</span> }
track dla tablic obiektów (zalecane: track po id)
export class AppComponent { produkty = [ { id: 1, nazwa: 'Laptop', cena: 3499 }, { id: 2, nazwa: 'Myszka', cena: 89 }, { id: 3, nazwa: 'Klawiatura', cena: 149 }, ]; }
<!-- Tablica obiektów – trackujemy po unikalnym id --> @for (produkt of produkty; track produkt.id) { <div class="card mb-2"> <div class="card-body"> <h5>{{ produkt.nazwa }}</h5> <p>Cena: {{ produkt.cena }} zł</p> </div> </div> }
| Typ tablicy | Co podać w track? | Przykład |
|---|---|---|
| Stringi / liczby | Samą zmienną iterującą | track owoc |
Obiekty z polem id |
Pole id obiektu | track produkt.id |
| Gdy nie ma id | Indeks jako ostateczność | track $index |
Iteracja po tablicy obiektów
W prawdziwych aplikacjach rzadko iterujemy po prostych stringach. Najczęściej mamy tablicę obiektów – każdy obiekt to jeden rekord danych, np. uczeń, produkt, zamówienie.
export class AppComponent { uczniowie = [ { id: 1, imie: 'Anna', klasa: '3TI', srednia: 4.8 }, { id: 2, imie: 'Piotr', klasa: '3TI', srednia: 3.5 }, { id: 3, imie: 'Karolina', klasa: '2TI', srednia: 5.0 }, { id: 4, imie: 'Marek', klasa: '2TI', srednia: 2.8 }, ]; }
<table class="table table-striped"> <thead> <tr> <th>Imię</th> <th>Klasa</th> <th>Średnia</th> </tr> </thead> <tbody> <!-- Każdy uczeń = jeden wiersz tabeli --> @for (uczen of uczniowie; track uczen.id) { <tr> <td>{{ uczen.imie }}</td> <td>{{ uczen.klasa }}</td> <td> <!-- @if wewnątrz @for – wyświetla odznakę dla najlepszych --> @if (uczen.srednia >= 4.75) { <span class="badge bg-success">{{ uczen.srednia }}</span> } @else { {{ uczen.srednia }} } </td> </tr> } </tbody> </table>
Możesz swobodnie zagnieżdżać @if wewnątrz @for
i odwrotnie. To jeden z najczęstszych wzorców w Angularze –
wyświetlasz listę i dla każdego elementu decydujesz co pokazać
w zależności od jego wartości.
Zmienne pomocnicze pętli
Angular udostępnia w bloku @for specjalne zmienne pomocnicze
zaczynające się od $. Dają informacje o aktualnym miejscu
w pętli – indeks, czy to pierwszy element, czy ostatni i inne.
$index, $first, $last
export class AppComponent { kroki: string[] = [ 'Pobierz Node.js', 'Zainstaluj Angular CLI', 'Utwórz projekt ng new', 'Uruchom ng serve' ]; }
@for (krok of kroki; track krok; let i = $index; let pierwszy = $first; let ostatni = $last) { <div class="d-flex align-items-center mb-2"> <!-- Numer kroku z $index (liczy od 0, dodajemy 1) --> <span class="badge bg-primary me-2">{{ i + 1 }}</span> <!-- Treść kroku --> <span>{{ krok }}</span> <!-- Odznaka tylko dla pierwszego elementu --> @if (pierwszy) { <span class="badge bg-success ms-2">Start</span> } <!-- Odznaka tylko dla ostatniego elementu --> @if (ostatni) { <span class="badge bg-warning ms-2">Koniec</span> } </div> }
$even, $odd, $count
@for (uczen of uczniowie; track uczen.id; let parzyste = $even; let nieparzyste = $odd; let liczba = $count) { <!-- Naprzemienne tło wierszy tabeli przez $even / $odd --> <tr class="{{ parzyste ? 'table-light' : '' }}"> <td>{{ uczen.imie }}</td> </tr> } <!-- $count – łączna liczba elementów w tablicy --> <p>Łącznie uczniów: {{ liczba }}</p>
| Zmienna | Typ | Wartość |
|---|---|---|
$index | number | Indeks bieżącego elementu (od 0) |
$first | boolean | true dla pierwszego elementu |
$last | boolean | true dla ostatniego elementu |
$even | boolean | true dla parzystych indeksów (0, 2, 4…) |
$odd | boolean | true dla nieparzystych indeksów (1, 3, 5…) |
$count | number | Całkowita liczba elementów w tablicy |
Po średniku i słowie track dodajesz kolejny średnik,
a po nim deklaracje zmiennych oddzielone przecinkami:
let nazwaWlasna = $nazwaSystemowa.
Nazwa własna może być dowolna – to Ty ją wybierasz.
Blok @empty – pusta lista
Co jeśli tablica jest pusta? Bez obsługi tego przypadku użytkownik
zobaczyłby po prostu pusty obszar – bez żadnej informacji.
@empty pozwala wyświetlić komunikat gdy tablica nie ma elementów.
export class AppComponent { koszyk: { id: number; nazwa: string }[] = []; // Tablica pusta – koszyk jest na razie pusty dodajProdukt(): void { this.koszyk.push({ id: this.koszyk.length + 1, nazwa: 'Produkt' }); } }
<button class="btn btn-primary mb-3" (click)="dodajProdukt()"> Dodaj do koszyka </button> <ul class="list-group"> @for (produkt of koszyk; track produkt.id) { <li class="list-group-item">{{ produkt.nazwa }}</li> } @empty { <!-- Ten blok pojawi się gdy koszyk = [] --> <li class="list-group-item text-muted text-center"> 🛒 Koszyk jest pusty </li> } </ul>
Przed Angularem 17 trzeba było pisać:
@if (koszyk.length === 0) { ... } osobno od pętli.
Teraz @empty obsługuje ten przypadek elegancko,
bezpośrednio w bloku @for.
Stary sposób – *ngFor
Tak jak @if zastąpił *ngIf, tak @for
zastąpił dyrektywę *ngFor. Poznaj ją, bo spotkasz ją w starszych
projektach i tutorialach.
🕰️ Stary sposób – *ngFor
<!-- Wymagało importu NgFor --> <li *ngFor="let owoc of owoce; let i = index" > {{ i + 1 }}. {{ owoc }} </li>
✅ Nowy sposób – @for (Angular 21)
<!-- Bez importów, czytelniejszy zapis --> @for (owoc of owoce; track owoc; let i = $index) { <li>{{ i + 1 }}. {{ owoc }}</li> }
| *ngFor (stary) | @for (nowy) | |
|---|---|---|
| Import wymagany? | Tak – NgFor lub CommonModule |
Nie |
track obowiązkowy? |
Nie (opcjonalny trackBy) |
Tak – wymuszony przez Angular |
| Pusta lista | Osobny *ngIf |
Wbudowany @empty |
| Zmienne pomocnicze | let i = index |
let i = $index |
Częste błędy
❌ Błąd 1 – Brak track
❌ Błąd kompilacji
<!-- Angular zgłosi błąd – track jest obowiązkowy! --> @for (owoc of owoce) { <li>{{ owoc }}</li> }
✅ Z track
@for (owoc of owoce; track owoc) { <li>{{ owoc }}</li> }
❌ Błąd 2 – Odwołanie do zmiennej poza blokiem
❌ owoc niedostępny poza @for
@for (owoc of owoce; track owoc) { <li>{{ owoc }}</li> } <!-- Poza blokiem – owoc nie istnieje! --> <p>{{ owoc }}</p> <!-- ❌ błąd -->
✅ Użyj osobnej zmiennej w klasie
// .ts – zapamiętaj wybrany element wybranyOwoc: string = ''; <!-- .html --> @for (owoc of owoce; track owoc) { <li (click)="wybranyOwoc = owoc">{{ owoc }}</li> } <p>Wybrano: {{ wybranyOwoc }}</p> <!-- ✅ działa -->
❌ Błąd 3 – track $index dla dynamicznych list
❌ Zły track gdy lista się zmienia
<!-- Gdy usuwasz element z środka listy, wszystkie indeksy poniżej się zmieniają --> @for (p of produkty; track $index) { ... }
✅ Trackuj po unikalnym id
<!-- id nie zmienia się – Angular wie co usunięto --> @for (p of produkty; track p.id) { ... }
Zadanie – lista zadań (todo list)
Stwórz prostą listę zadań używając @for, @if
i zmiennych szablonowych. To połączenie wszystkiego czego nauczyłeś się do tej pory.
W klasie zdefiniuj:
- Tablicę obiektów
zadaniaz polamiid,tresc,ukonczone(boolean) – wstaw 3–4 przykładowe zadania - Metodę
dodajZadanie(tresc: string)dodającą nowe zadanie do tablicy - Metodę
przelaczStatus(id: number)zmieniającą poleukonczonena przeciwne
W szablonie wyświetl:
- Formularz z polem tekstowym (
#noweZadanie) i przyciskiem „Dodaj” - Listę zadań przez
@forztrack zadanie.id - Dla każdego zadania: numer (
$index + 1), treść, przycisk „Zrobione/Cofnij” - Zadania ukończone wyświetl przekreślone (Bootstrap: klasa
text-decoration-line-through) – użyj@if - Gdy lista jest pusta – komunikat przez
@empty - Pod listą wyświetl: „Ukończono X z Y zadań” używając
$count
W następnym materiale poznamy Property Binding – wiązanie
właściwości elementów HTML ze zmiennymi TypeScript przez nawiasy kwadratowe
[ ].