Kurs Angular · Moduł 9: HttpClient
Wzorce HTTP - trzy pokolenia
W tej lekcji3
Każda aplikacja robi w kółko to samo: pobierz dane z serwera i pokaż jeden z trzech stanów - ładowanie, błąd albo wynik. Angular przez lata dawał na to coraz krótsze sposoby. W tej lekcji zobaczysz trzy pokolenia tego samego zadania - od ręcznej roboty po Resource API, które robi wszystko za Ciebie. Ta sama myśl, coraz mniej kodu.
Pokolenie pierwsze: jeden serwis CRUD dla wszystkiego
Zanim zajmiemy się stanami, uprzątnijmy powtarzalność. Każdy byt (samuraj, klan, miecz) potrzebuje tych samych pięciu operacji: pobierz wszystkie, pobierz jeden, utwórz, zmień, usuń. Zamiast pisać je pięć razy, tworzysz jeden generyczny serwis.
@Injectable({ providedIn: 'root' })
export class ApiService<T extends { id: number }> {
constructor(private http: HttpClient, private baseUrl: string) {}
getAll(): Observable<T[]> {
return this.http.get<T[]>(this.baseUrl);
}
getById(id: number): Observable<T> {
return this.http.get<T>(`${this.baseUrl}/${id}`);
}
create(item: Omit<T, 'id'>): Observable<T> {
return this.http.post<T>(this.baseUrl, item);
}
update(item: T): Observable<T> {
return this.http.put<T>(`${this.baseUrl}/${item.id}`, item);
}
}
// Konkretny serwis dziedziczy komplet operacji
@Injectable({ providedIn: 'root' })
export class SamuraiApiService extends ApiService<Samurai> {
constructor(http: HttpClient) {
super(http, '/api/samurai');
}
}Sekret to <T extends { id: number }> - serwis jest generyczny, działa dla dowolnego typu, byle miał id. SamuraiApiService tylko dziedziczy i podaje swój adres, a dostaje pięć gotowych metod za darmo. Omit<T, 'id'> przy tworzeniu jest sprytny: nowy samuraj nie ma jeszcze id (nada je serwer), więc typ wprost tego zabrania.
Pokolenie drugie: trzy stany na sygnałach
Teraz sedno - pokazanie stanu. Ręcznie trzymasz trzy sygnały: dane, "trwa ładowanie" i "wystąpił błąd". Sam je przełączasz w zależności od tego, co zwróci serwer.
export class SamuraiListComponent {
samuraiList = signal<Samurai[]>([]);
loading = signal(true);
error = signal<string | null>(null);
loadSamurai(): void {
this.loading.set(true);
this.error.set(null);
this.samuraiService.getAll().pipe(takeUntilDestroyed()).subscribe({
next: (samurai) => { this.samuraiList.set(samurai); this.loading.set(false); },
error: (err) => { this.error.set(err.message); this.loading.set(false); }
});
}
}Prześledź trzy stany: przed żądaniem ustawiasz loading na true, gdy dane przyjdą (next), zapisujesz je i gasisz ładowanie, a gdy serwer zwróci błąd (error), zapisujesz komunikat i też gasisz ładowanie. W szablonie @if (loading()) / @else if (error()) / @else pokazuje właściwy widok. Działa - ale zauważ, ile ręcznego przełączania: trzy sygnały i pamiętanie, by wszędzie zgasić loading. To woła o coś prostszego.
Pokolenie trzecie: Resource API robi to za Ciebie
Angular 19 zauważył, że ten wzorzec trzech stanów jest zawsze taki sam - i zamknął go w resource(). Podajesz tylko skąd wziąć dane, a stany dostajesz gotowe.
import { resource } from '@angular/core';
export class SamuraiDetailComponent {
id = input.required<number>();
samuraiResource = resource({
request: () => this.id(),
loader: async ({ request: id }) => {
const response = await fetch(`/api/samurai/${id}`);
if (!response.ok) throw new Error('Failed to load');
return response.json() as Promise<Samurai>;
}
});
}Porównaj z poprzednim przykładem: zniknęły trzy ręczne sygnały. resource sam wystawia status() (loading/error/success), value() i error() - w szablonie robisz na nich @switch. Co więcej, request śledzi sygnał id: gdy id się zmieni, resource sam pobierze dane od nowa. To samo zadanie co w pokoleniu drugim, ale bez ani jednego ręcznego set.
Pamiętaj z tej lekcji nie składni, lecz kierunku: każde pobranie danych to trzy stany - ładowanie, błąd, wynik. Kiedyś przełączałeś je ręcznie trzema sygnałami, dziś resource() robi to za Ciebie. Gdy widzisz ten wzorzec, wybierasz tylko, ile chcesz napisać sam.
Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Do czego służy HttpParams?
2. Co oferuje funkcja resource() wprowadzona w Angularze 19?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Edytor kodu
Uzupełnij metodę wyszukiwania tak, by wysyłała parametry zapytania (q, page) i własny nagłówek.
- Układanie w poziomie
Ułóż tokeny jednej linii tworzącej parametr zapytania q:
- Układanie w pionie
Uporządkuj kolejne kroki ładowania danych do sygnałów w komponencie:
- Układanie w pionie
Uporządkuj bloki szablonu obsługującego stany resource() w kolejności: nagłówek switch, ładowanie, błąd, sukces:
- Klikanie w kolejności
Zbuduj wywołanie ponownie ładujące dane zasobu resource():
- Układanie w poziomie
Ułóż tokeny jednej linii wywołującej GET z obiektem opcji params:
- Klikanie w kolejności
Zbuduj początek deklaracji zasobu w komponencie (do otwarcia obiektu konfiguracji):