Wersja 20.1 Angulara wprowadza zestaw ciekawych usprawnień. Przeanalizujemy takie tematy, jak uproszczony kod w templatkach i testach, nowe możliwości w zarządzaniu requestami HTTP czy renderowanie obrazków.
Binarne operatory przypisania w templatkach
W Angularze 20.1 wprowadzono długo wyczekiwaną funkcję: wsparcie dla binarnych operatorów przypisania bezpośrednio w szablonach komponentów. Są to:
- +=, -=, *=, /=, %=
- **=, <<=, >>=, >>>=
- &=, ^=, |=
- &&=, ||=, ??=
Dotychczas, jeśli chcieliśmy zaktualizować wartość zmiennej w szablonie (np. zwiększyć licznik), musieliśmy to robić za pomocą dedykowanej metody w komponencie:
<button (click)="increment()">+</button>
increment() {
this.counter += 1;
}
Od Angulara 20.1 można to zapisać znacznie prościej:
user = { name: '' };
<button (click)="user.name ??= 'Anonymous'">Set user name</button>
<button (click)="counter += 1">+</button>
Dzięki temu:
- mamy mniej kodu w klasie komponentu
- proste operacje są widoczne bezpośrednio w szablonie
Ulepszony NgOptimizedImage
Dyrektywa NgOptimizedImage dostała nowy parametr decoding, który pozwala lepiej kontrolować sposób dekodowania obrazów. Zwiększa to kontrolę nad wydajnością i sposobem ładowania obrazów, przy czym jest zgodne ze standardami HTML.
<img ngSrc="..." decoding="async" />
Parametr ten posiada 3 opcje:
- sync – dekoduje obrazek od razu podczas ładowania kontentu
Może być użyte, gdy zależy nam na wyświetleniu obrazka precyzyjnie w momencie renderowania
- async – dekodowanie asynchroniczne
Zapewnia płynność interfejsu, bo obraz jest dekodowany w tle, nie blokując interakcji
- auto – automatyczne
Przeglądarka sama wybiera najlepszy sposób
Nowe parametry w HttpResource i HttpClient
Wsparcie dla cache i priority
W Angularze 20.1 rozszerzono HttpResource o dodatkowe opcje: cache i priority, które odpowiadają parametrom znanym z Fetch API.
cache decyduje o tym, czy i jak przeglądarka może użyć swojej wewnętrznej pamięci podręcznej, gdy wykonujemy request HTTP. Angular tego wcześniej nie wspierał wprost – teraz możemy to określić jawnie.
Przydatne jest to np. wtedy, gdy chcemy wymusić świeże dane z API, bez polegania na buforze przeglądarki.
Możliwe wartości:
- default – Przeglądarka sama zdecyduje
- no-store – Żadne dane nie będą zapisywane ani odczytywane z cache
- reload – Zawsze pobierze z sieci (ignoruje cache)
- force-cache – Użyje cache, nawet jeśli jest przestarzały
- only-if-cached – nakazuje przeglądarce wykonać request tylko wtedy, gdy znajdzie pasującą odpowiedź w cache – tzn. jeśli już chociaż raz wysłał taki request. Jeśli nie wysłał – ten request nie zostanie wysłany w ogóle, a przeglądarka zwróci błąd.
httpResource({
getConfig: () => ({
url: '/api/products',
cache: 'reload'
})
});
priority to nowy, eksperymentalny parametr, który pozwala określić jak ważny jest dany request, co teoretycznie może wpłynąć na to, jak szybko zostanie obsłużony.
Możemy go użyć np. gdy mamy wiele równoległych fetchów ii chcemy, by niektóre były obsługiwane wcześniej.
Dostępne opcje:
- high – Najwyższy priorytet (np. dane do szybkiego wyrenderowania interfejsu)
- low – Niski priorytet (np. preloady, dane drugoplanowe)
- auto – Domyślne zachowanie przeglądarki
httpResource({
getConfig: () => ({
url: '/api/metrics',
priority: 'low'
})
});
Nie wszystkie przeglądarki to jeszcze wspierają (Internet Explorer, Chrome < 103, Firefox < 132, Safari < 17.2, Edge < 103), ale daje to Angularowi otwartą drogę do przyszłych optymalizacji.
Wsparcie dla mode i redirect
Od Angulara 20.1 możemy teraz ustawić opcje mode i redirect w konfiguracji HttpResource, co pozwala precyzyjnie kontrolować sposób komunikacji z API — zwłaszcza z zewnętrznymi usługami (np. API innych domen).
mode określa jak przeglądarka ma traktować żądanie pod względem polityki dostępu do zasobów z innych domen (CORS).
Dostępne opcje:
- cors – Pozwala na dostęp do zasobów z innej domeny (o ile serwer ustawi odpowiednie nagłówki CORS)
- same-origin – Pozwala tylko na requesty do tej samej domeny
- no-cors – Pozwala na requesty do innych domen, ale bez możliwości odczytania odpowiedzi (bardzo ograniczone)
httpResource({
getConfig: () => ({
url: 'https://api.domena-zewnetrzna.com/data',
mode: 'cors'
})
});
redirect decyduje, jak przeglądarka powinna zachować się przy odpowiedzi typu 301, 302, 307 itp. Pozwoli to np. wykryć automatyczne przekierowywanie.
Dostępne wartości:
- follow – Automatycznie śledzi przekierowania (domyślne zachowanie)
- error – Rzuca błąd, jeśli napotka przekierowanie
- manual – Przeglądarka nie śledzi przekierowania — programista może je obsłużyć samodzielnie
httpResource({
getConfig: () => ({
url: '/api/old-endpoint',
redirect: 'error'
})
});
Rozszerzenie o credentials
credentials to opcja, która kontroluje czy i kiedy przeglądarka dołącza ciasteczka (cookies), nagłówki uwierzytelniające (Authorization) i inne dane sesji do żądania HTTP — zwłaszcza przy żądaniach między różnymi domenami.
To ważne w przypadku:
- uwierzytelniania sesyjnego (np. przez cookies HTTP-only)
- API wymagających identyfikacji klienta
- połączeń cross-origin (np. z backendem na innej domenie, porcie lub subdomenie)
Dostępne opcje:
- same-origin – Domyślna opcja: tylko jeśli frontend i backend są na tej samej domenie
- include – zawsze wysyła ciasteczka i dane uwierzytelniające, w tym do innej domeny
- omit – nigdy nie wysyła ciasteczek ani nagłówków autoryzacyjnych
this.http.get('/api/user', {
fetchOptions: {
credentials: 'include'
}
});
Wsparcie dla keepalive
Angular dodaje obsługę opcji keepalive do eksperymentalnego API HttpResource, które jest oparte na niskopoziomowym fetch(). Dzięki temu można teraz oznaczać requesty jako:
{ keepalive: true }
co umożliwia ich wysyłanie nawet w momencie rozładowywania strony (np. zamknięcie karty, przejście na inną stronę). Co prawda ta opcja jest ograniczona do niewielkich requestów POST lub GET niezawierających body (do 64KB), ale będzie przydatna, np do zapisywania metryk, czy czyszczenia sesji.
const resource = httpResource({
method: 'POST',
url: '/api/session/close',
keepalive: true,
});
Injection context w loadComponent() i loadChildren()
Od teraz możemy bez błędów wstrzykiwać różne zależności, np. serwis, przed wykonaniem loadChildren() i loadComponent() i na nich operować.
{
path: 'home',
loadComponent: () => {
const config = inject(MyFeatureConfig); // wcześniej błąd!
return import('./home.component').then(m => m.HomeComponent);
}
}
Opcja bindings w TestBed
Pisanie testów jednostkowych z użyciem TestBed stanie się teraz troszkę prostsze, ponieważ nowa zmiana pozwala przekazywać np. inputy czy @HostBinding() bezpośrednio przy tworzeniu komponentu testowego.
Do tej pory aby przetestowac komponent musieliśmy m.in. ręcznie ustawić ustawić każdy input po stworzeniu tego komponentu.
it('old', () => {
const fixture = TestBed.createComponent(MyComponent);
fixture.componentInstance.title = 'Hello';
fixture.componentInstance.hostClass = 'highlighted';
fixture.detectChanges();
const el: HTMLElement = fixture.nativeElement;
expect(el.textContent).toContain('Hello');
expect(el.className).toBe('highlighted');
});
Teraz będziemy mogli to zrobić czytelniej:
it('new', () => {
const fixture = TestBed.createComponent(MyComponent, {
bindings: {
title: 'Hello',
class: 'highlighted',
}
});
const el: HTMLElement = fixture.nativeElement;
expect(el.textContent).toContain('Hello');
expect(el.className).toBe('highlighted');
});
W skrócie – nowa opcja bindings:
- pozwala od razu przekazać inputy i host-bindingi
- eliminuje potrzebę pisania fixture.componentInstance.xxx = …
- sprawia, że testy są bardziej deklaratywne
DestroyRef.destroyed
DestroyRef to wprowadzony wcześniej mechanizm Angulara (od v16), który pozwala reagować na cykl życia instancji (np. komponentu, serwisu) bez potrzeby implementowania ngOnDestroy().
Nowa właściwość destroyed pozwala sprawdzić, czy dana instancja została już zniszczona, czy jeszcze żyje.
Poniższy przykład mówi o przypadku, kiedy mamy otwarty search z ustawionym opóźnieniem. W momencie gdy search zostaje z jakiegoś powodu zamknięty (tutaj zniszczony) – nie wykonamy niepotrzebnie funkcji preformSearch(), bo komponent już nie istnieje.
search.valueChanges.pipe(debounceTime(300)).subscribe(value => {
if (!this.destroyRef.destroyed) {
this.performSearch(value);
}
});
Tym samym unikamy błędów typu ExpressionChangedAfterItHasBeenCheckedError, bo nie operujemy np. na zmiennych, które również zostały zniszczone.
Podsumowanie
Wersja 20.1 Angulara to przede wszystkim kolejne kroki w kierunku bardziej ergonomicznego i wydajnego tworzenia aplikacji. Zmiany nie są duże, ale znacząco wpływają na komfort pracy i stabilność projektów.
Pamiętajmy także o zaktualizowaniu wersji Node.js do minimum 20.19, 22.12, lub 24.0, ponieważ nowa wersja Angulara takich właśnie wymaga.