Wróć do strony głównej
Angular

Angular 15 (14+) – co nowego?

Po bardzo udanym release v14, zespół Angulara nie zwalnia tempa i również wersja  Angular 15 dostarcza nam wiele nowych, ciekawych feature’ów. Oprócz tego, także pomiędzy głównymi wersjami (a więc w 14.X) pojawiło się trochę wartych uwagi nowości, o których nie wspominaliśmy dotąd na łamach naszego bloga. Poniżej znajdziecie więc informacje o najważniejszych z nich.

Stabilne standalone APIs

Począwszy od wersji 15, standalone APIs porzucają etykietę developer preview i zyskują miano stabilnych. Dostajemy więc zielone światło dla bezpiecznego stosowania ich w swoich aplikacjach, także produkcyjnie. W dalszej części artykułu przekonamy się, że wiele kolejnych zmian bazuje właśnie na tym fakcie.

Jeśli jeszcze tego nie zrobiliście, zachęcam także do zapoznania się z naszym artykułem o samych standalone komponentach: Angular Standalone API.

Router

Angular 14.1 przyniósł nam nowy rodzaj guarda – canMatch. Jaka jest zatem różnica pomiędzy wyżej wymienionym a canLoad (ten mówi nam, czy można załadować route, który referuje do leniwie ładowanego modułu) oraz canActivate/canActivateChild (mówią odpowiednio, czy możemy aktywować route/route będący dzieckiem)? CanMatch działa niejako na innym, “wcześniejszym” etapie i decyduje, czy obecny url może być dopasowany do danego route. Może więc pełnić podobną rolę, co oba canLoad (którego swoją drogą docelowo zastąpi) jak i canActivate*, natomiast gdy zwróci false procesowane są kolejne wpisy w konfiguracji routingu.

Zyskujemy więc nową możliwość – definiowania route’a z tą samą ścieżką, ale nawigującego do różnych miejsc na podstawie zaimplementowanego warunku. Jest to przydatne np. przy obsłudze różnych ról użytkowników, czy warunkowego ładowania innej implementacji na podstawie feature flag. Przykład użycia poniżej:

Na zmiany w routerze spowodowane wprowadzeniem standalone components nie przyszło czekać długo, bowiem już w wersji 14.2 pojawiły się API wspierające podejście module-less.

W praktyce oznacza to, że nasza aplikacja nie musi już w ogóle korzystać z RouterModule w celu implementacji routingu. Zamiast tego otrzymujemy cały zestaw alternatywnych API (które przy okazji mają zaletę bycia łatwo treeshake’owalnymi):

Pełną listę funkcji i opcji, jakich możemy użyć z provideRouter znajdziemy w dokumentacji.

Kolejną nowością wprowadzoną w tej samej wersji do tego obszaru jest wsparcie dla funkcyjnych guardów i resolverów. Oznacza to możliwość ich implementacji w postaci zwykłych funkcji, a więc można całkowicie pożegnać się z klasami mającymi tutaj do tej pory wyłączność. Wprowadzenie tego konceptu zrodziło wiele reakcji wśród community, zarówno pozytywnych jak i negatywnych. Część developerów uznała to za zwiastun nowego kierunku, w którym zmierza framework. Abstrahując od osobistych preferencji, nowemu rozwiązaniu nie sposób odmówić jednego – wymaga o wiele mniej boilerplate’u. W dodatku bardzo łatwo można teraz tworzyć funkcje-fabryki przyjmujące jakiś parametr i zwracające na tej podstawie odpowiednio skonfigurowaną wersję guarda. Użycie wygląda następująco (można łatwo porównać z wcześniejszym przykładem):

HttpClient

Również obszar komunikacji za pośrednictwem HTTP doczekał się pewnych zmian w wersji Angular 15.

Pierwszą z nich są, podobnie jak w przypadku routera, API dla podejścia module-less.

Ponownie, pełną listę funkcji i opcji, jakich możemy użyć z provideHttpClient znajdziemy w dokumentacji.

Drugą nowością, znowu analogicznie do routera, jest wsparcie dla funkcyjnych interceptorów. Użycie wygląda następująco:

Directive Composition

Jedną z najbardziej oczekiwanych funkcji we frameworku była możliwość reużycia dyrektyw i aplikacji ich zachowań do innych dyrektyw czy komponentów. Wersja 15 przyniosła ze sobą implementację właśnie takiego mechanizmu.

Do tej pory możliwości (częściowego) osiągnięcia podobnego rezultatu były niewielkie, np. wykorzystanie dziedziczenia, gdzie głównym ograniczeniem jest możliwość korzystania tylko z jednej klasy bazowej. Innym pomysłem, stosowanym choćby przez Angular Material, jest użycie TypeScriptowych mixinów, co wymusza jednak specyficzne podejście do współdzielonego w ten sposób kodu, mocno komplikuje implementację i wyłącza korzystanie z pełni API Angulara w mixinach.

Obecna implementacja jest bardzo elastyczna i otwiera całe spektrum nowych możliwości – ogranicza nas głównie własna kreatywność.

Jedyną istotną restrykcją jest możliwość aplikowania wyłącznie standalone dyrektyw do naszych dyrektyw/komponentów (te nie muszą już być standalone).

Przykład użycia prezentuje się następująco:

Powyższy kawałek kodu aplikuje dyrektywy NgClass i CdkDrag do naszego komponentu. Pierwsza z nich nie eksponuje żadnych inputów/outputów, a więc nie będzie można ich użyć w template w miejscu użycia naszego komponentu. Druga natomiast eksponuje zarówno input jak i output, przy czym dla outputu definiuje alias. W związku z tym użycie naszego komponentu mogłoby wyglądać jak poniżej:

Dyrektywa NgClass aplikuje więc jedynie swoje domyślne zachowanie do komponentu (co w przypadku akurat NgClass oznacza brak jakichkolwiek zmian). Czy możemy więc w jakikolwiek sposób sterować jej zachowaniem? Jest to możliwe używając funkcji inject, która pozwala nam wstrzyknąć do komponentu instancję dyrektywy i manipulować jej właściwościami z poziomu samego komponentu:

Poniżej znajdziecie prosty, interaktywny przykład implementacji poszczególnych elementów opisanych wyżej. Warto zwrócić uwagę na zachowanie aliasów inputów/outputów przy aplikacji kilku dyrektyw.

Jako bonus kilka pomysłów wykorzystania directive composition:

https://twitter.com/_crisbeto/status/1582475442715385858
https://twitter.com/BartBurgov/status/1582692518986100736
https://twitter.com/NetanelBasal/status/1581614761212796935
https://twitter.com/i_beqiri21/status/1592434518291808261
https://twitter.com/ArmanOzak/status/1597279998716481536

Usprawnienia w obszarze Developer Experience

Stack traces & debugging

Usprawnienia w tym obszarze są możliwe dzięki kooperacji zespołu Angulara i zespołu Chrome. Podstawą jest tu możliwość oznaczania skryptów jako “zewnętrzne” i w ten sposób wykluczania ich w funkcjach narzędzi developerskich (m.in. właśnie ze stack trace’ów). Począwszy od Angulara 14.1 w ten sposób oznaczona jest zawartość folderów node_modules i webpack. Warto nadmienić, że jest to mechanizm dostępny dla wszystkich developerów, więc choćby autorzy innych frameworków mogą z niego również skorzystać.

Wracając do Angulara, w rezultacie przy wyświetlaniu błędów w konsoli otrzymujemy ścieżkę wywołań pomijającą skrypty, które prawdopodobnie nie leżą w obszarze zainteresowań developera (np. zone.js). Także podczas debugowania i iterowania po kolejnych instrukcjach w kodzie, te należące do wykluczonych skryptów są pomijane.

stack trace
(screenshot z dotychczasową postacią nie zostanie zamieszczony, gdyż jak widać na podstawie ostatniego linku powyżej, zawiera grubo ponad 200 linii)

Drugą wartą wspomnienia nowością jest zachowanie ciągłości ścieżki wywołania przy operacjach asynchronicznych – kod wykonywany w następstwie zakończenia akcji asynchronicznej może być teraz prawidłowo łączony z kodem inicjującym tę akcję (np. kliknięciem i zawołaniem do serwera) – jest to możliwe dzięki wprowadzeniu w Chrome mechanizmu tzw. Async Stack Tagging API.

linked call stackvsunlinked call stack

Lazy loading

Wersja 15 przynosi także drobną zmianę jeśli chodzi o syntax lazy loadingu – od teraz import elementów, które są oznaczone domyślnym exportem z danego pliku, może być nieco krótszy w zapisie (dotyczy to zarówno loadChildren jak i loadComponent).

Language Service

Kolejnym usprawnieniem związanym z DX jest możliwość automatycznego importu komponentów, których selektora użyliśmy w template innego komponentu (dotyczy zarówno standalone jak i module-based).

language service component imports Angular 15

Podsumowanie

Niniejszy artykuł pokazuje, że od wprowadzenia wersji 14.0 w Angularze zaszło wiele istotnych i interesujących zmian. Co więcej, artykuł ten opisuje jedynie podzbiór najważniejszych z nich, nie wspominając np. wprowadzenia alternatywnego buildera opartego o esbuild, dyrektywy NgOptimizedImage, czy kompletnej przebudowy Angular Material (które to tematy zasługują na osobne artykuły).

Czego powinniśmy spodziewać się w przyszłości? Już niebawem możemy liczyć na wypuszczenie Angulara 15.1, gdzie m.in. pojawi się wsparcie dla TypeScripta 4.9, oznaczenie guardów canLoad jako deprecated, a także dalsze usprawnienia w obszarze Language Service.

Natomiast jeśli chodzi o dalszą przyszłość, tutaj warto zajrzeć do roadmapy, która także została odświeżona z początkiem listopada. Zawiera ona wiele dość “gorących” tematów takich jak – nareszcie – usprawnienia dla SSR, czy wsparcie dla podejścia zone-less i lokalnej detekcji zmian (prawdopodobnie opartej o koncepcję sygnałów). Liczymy na to, że już niedługo będziemy mogli informować Was o tych nowościach na naszym blogu!

Inne wersje

Sprawdź zmiany w innych wersjach Angular:

O autorze

Krzysztof Skorupka

Angular Team Leader w House of Angular. Dowodzi członkami zespołów pomagając im rozwinąć się w tworzeniu oprogramowania na najwyższym poziomie, a jednocześnie sam tworzy aplikacje internetowe Angular. Interesuje się także analizą biznesową i architekturą aplikacji webowych dostosowanych do potrzeb klienta.

Zapisz się do naszego newslettera. Bądź na bieżąco z najnowszymi trendami, poradami, meetupami i stań się częścią społeczności Angulara w Polsce. Rynek pracy docenia członków społeczności.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *