W minionym tygodniu wydany został Angular 15.1. Wraz z nim do Angulara trafiły od dawna wyczekiwane Self Closing Tags. To jednak nie koniec nowości.
1. Angular 15.1
W minionym tygodniu wydany został Angular 15.1. Większość zmian i nowości dotyczy funkcjonalności, które dodane zostały w Anuglarze 14 i 15. Bez zbędnego przedłużania przejdźmy do rzeczy.
Self Closing Tags
Frameworki takie jak Vue czy React, traktują komponenty jako czysto abstrakcyjne byty mapowane do struktury DOM. Jeśli zobaczycie sobie plik HTML wygenerowany przez jeden z tych frameworków, to nie znajdziecie tam właściwie żadnego śladu po abstrakcji jaką są kompoenenty. Angular podchodzi do tematu zupełnie inaczej i każdemu komponentowi przypisuje Custom HTML Tag. Dzięki temu kiedy spojrzycie na stronę wygenerowaną przez Angulara, od razu będziecie w stanie powiązać odpowiednie fragmenty HTML z komponentami.
<!-- The HTML code of a simple application rendered by Angular -->
<!-- You can clearly see how source code is devided into components -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Simple App</title>
<script src="script.js"></script>
</head>
<body>
<app-root>
<h2>Welcome to my application!</h2>
<app-navigation-link>
<a href="/home">Home</a>
<app-navigation-link>
<app-navigation-link>
<a href="/about">About</a>
<app-navigation-link>
<app-root>
</body>
</html>
<!-- The same application as above but this time rendered with react -->
<!-- It's really hard to say source how code have been devided into componets -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Simple App</title>
<script src="script.js"></script>
</head>
<body>
<div>
<h2>Welcome to my application!</h2>
<a href="/home">Home</a>
<a href="/about">About</a>
<div>
</body>
</html>
Specyfikacja Custom HTML Tag zakłada kilka ograniczeń. Między innymi nazwa tagu musi zawierać znak -
. To właśnie dlatego Angular domyślnie do selektorów dodaje prefix app
. Ograniczenie to nie więło się znikąd – przestrzeń nazw nie zawierających -
zarezewowana jest dla przyszłego wykorzystania w standardzie HTML.
Według standardu HTML, tylko wybrane tagi mogą zamykać same siebie. Ich lista jest stosunkowo krótka i zawiera takie elementy jak <link />
, <br />
czy <img />
(pełną listę możecie znaleźć między innymi tutaj). Niestety na liście tej na próżno szukać Custom HTML Tags. Oznacza to, że jeśli chcemy pozostać zgodni ze standardem HTML, to skazani jesteśmy na ręczne zamykanie Custom HTML Tags. Chcąc pozostać w zgodzie ze standarderm, Angular musiał pozostać za konkurencją, która od dawna umożliwiał takie praktyki (m.in. JSX’a i <App />
).
<!-- Below declarations are incorrect -->
<div /> <!-- ❌ div is not a self closing tag -->
<approot></approot> <!-- ❌ custom tags must contain - -->
<app-root /> <!-- ❌ custom tags are not self closing -->
<!-- Below declarations are correct -->
<div></div <!-- ✅ this is correct syntax -->
<app-root></app-root> <!-- ✅ this is correct syntax -->
W Angularze 15.1 deweloperom udało się w końcu znaleźć panaceum na zaistniały problem. Pisane przez nas templates nie trafiają w końcu bezpośrednio do użytkownika, ale są jeszcze transpilowane (jak inaczej mogłoby działać *ngFor
, *ngIf
czy <ng-template>
). To właśnie podczas tej transpilacji Angular podmieniał będzie samo zamykające się tagi do formatu zgodnego ze standardem. W ten sposób, pomimo że pisane przez nas templates nie będą zgodne ze standardem, to HTML wysyłany do klienta już jak najbardziej.
<!-- Angular 15.0 -->
<app-button
text="Click me!"
variant="filled"
(onClick)="handleClick($event)"
></app-button>
<app-button
text="Don't click me!"
variant="outline"
(onClick)="handleClick($event)"
></app-button>
<!-- Angular 15.1 -->
<app-button text="Click me!" variant="filled" (onClick)="handleClick($event)" />
<app-button text="Don't click me!" variant="outline" (onClick)="handleClick($event)" />
Deprekacja CanLoad
na rzecz CanMatch
Przed Angularem 15 do dyspozycji deweloperów były dwa guardy CanLoad
i CanActivate
. W przypadku zastosowania lazy-loadingu CanLoad
decydował czy dany fragment kodu może zostać załadowany przez przeglądarkę. Jeśli kod został już raz załadowany, to Guard ten nie jest uruchamiany ponownie. Z tego względu, jeśli chcemy ograniczyć dostęp do danej strony (na przykład dla nie zalogowanych użytkowników), to musimy dodatkowo zabzpieczyć go dodatkow CanActivate
.
const routes: Route[] = [{
path: '/me',
canLoad: [() => inject(UserService).isLoggedIn()],
canActivate: [() => inject(UserService).isLoggedIn()],
loadComponent: () => import('./user-page/user-page.component')
}];
Do Angulara 15 trafił nowy Guard CanMatch
, który wywoływany jest przy każdej próbie dopsowania ścieżki niezależnie od tego czy kod danego modułu został już załadowany. Dzięki temu podwójną definicję CanLoad
i CanActivate
zastąpić możemy jednym zgrabnym CanMatch
.
const routes: Route[] = [{
path: '/me',
canMatch: [() => inject(UserService).isLoggedIn()],
loadComponent: () => import('./user-page/user-page.component')
}];
CanMatch
różni się od CanLoad
jeszcze jednym aspektem. W przypadku dopasowania do ścieżki zabezpieczonej zarówno CanLoad
jak i CanActivate
, to programista odpowiedzialny jest za obsługę redirectów. Jeśli z CanMatch
zwrócone zostanie false
, to Angular kontynuował będzie iterowanie po tablicy zdefiniowanych route. Dzięki temu w prosty sposób możemy pod tym samym urlem hostować stronę dla administratora i zwykłego użytkownika, czy fallbackować do domyślnej strony jeśli użytkownik nie ma dostępu do aktualnie requestowanej.
const routes: Route[] = [
{
path: '/me',
canMatch: [() => inject(UserService).isAdmin()],
loadComponent: () => import('./admin-page/admin-page.component')
},
{
path: '/me',
canMatch: [() => inject(UserService).isLoggedIn()],
loadComponent: () => import('./user-page/user-page.component')
},
{
path: '**',
loadComponent: () => import('./not-logged-page/not-logged-page.component')
}
];
TestBed.runInInjectionContext
Funkcja inject()
, to potężne narzędzie dodane w Angularze 14, które może uczynić nasz kod znacznie czytelniejszym. To dzięki niej możlwie jest między innymi pisane functional guards takich jak ten poniżej.
function onlyLoggedInGuard(): boolean {
return inject(UserService).isLoggedIn();
}
const routes: Route[] = [{
path: '/me',
canMatch: [onlyLoggedInGuard],
loadComponent: () => import('./user-page/user-page.component')
}];
Do tej pory, aby przetestować guarda takiego jak ten powyżej musieliśmy odpowiednio wsztrzyknąć do niego kontekst Dependency Injection. Tajemniczy EnvironmentInjector
mocno psuł abstrakcję TestBed
, która pozwalała programistom zapomnieć o szczegółach implementacyjnych Dependency Injection.
describe('onlyLoggedInGuard', () => {
/* ... */
it('should not allow not logged in users', () => {
/* ... */
expect(
TestBed.inject(EnvironmentInjector).runInContext(onlyLoggedInGuard)
).toEqual(expectedValue);
})
});
W Angulara 15.1 TestBed rozszerzony został o runInInjectionContext
, który trochę upraszcza powyższą konfigurację.
describe('onlyLoggedInGuard', () => {
/* ... */
it('should not allow not logged in users', () => {
/* ... */
expect(
TestBed.runInInjectionContext(onlyLoggedInGuard)
).toEqual(expectedValue);
})
});
isStandalone()
Jeśli kiedykolwiek potrzebowaliście dowiedzieć się z poziomu API czy dany komponent, piepe lub dyrektywa została zdediniowana w tradycyjny sposób z modułami, czy też jako Standalone Componet, to od teraz będzie to możliwe.
@Component({
selector: 'app-standalone-component',
standalone: true
})
class StandaloneComponent {}
/* ... */
if(isStandalone(StandaloneComponent)) {
console.log(`StandaloneComponent is a standalone component`)
}
Zastanawiałem się chwilę, do czego mógłbym użyć nowego API, ale niestety nic nie przyszło mi do głowy. Jeśli Wy czekaliście na tą funkcjonalność i macie dla niej jakieś ciekawe zastosowanie, to koniecznie podzielcie się nimi z nami na Twitterze.
Zainstaluj teraz i czytaj tylko dobre teksty!
2. State of JS 2022
State of JS to największa ankieta dotycząca JavaScript. W minionym tygodniu opublikowane zostały jej wyniki. Jak co roku, każdemu polecam zapoznanie się z nimi samodzielnie, a ja poniżej dzielę się kilkoma moimi spostrzeżeniami:
- 70% respondentów to mężczyźni i tylko 2.4% respondendtów to kobiety. Mam nadzieję, że tajemnica tej dysproporcji kryje się w opcji
Brak odpowiedzi
, którą zaznaczyło aż 24% respondentów. Jeśli nie, to mamy spory problem… - Wśród frontendowych frameworków pod względem satysfakcji nowym królem jest Solid. Poprzedni król w postaci Svelte wciąż depcze mu jednak po plecach, bo różnica wynosi zaledwie jeden punkt procentowy (90,9% vs 89,7%). Najwyższy czas, żeby frameworki te w końcu zaczęły skracać dystans w stosunku do wielkiej trójki.
- Wśród frontendowych frameworków pod względem wykorzystania bez zmian – wciąż króluje wielka trójka (React, Angular i Vue). Warto jednak zauważyć, że Angular i Vue zaliczyły spadki o kilka punktów procentowych. W przypadku tego pierwszego, moze to być spowodowane niskimi wynikami jeśli chodzi o satysfakcję. W przypadku tego drugiego, głownego sprawcy doszukiwałbym się w Vue 3, które wprowadził sporo breaking changes i tym samym dało deweloperom świetny pretekst do migracji.
- Astro wbiło sie na pierwsze miejsce pod względem satysfakcji z frameworków do rendrowania po stronie serwera. To kolejny artgument za tym, żebyście zainteresowali się tym frameworkiem i koncpecją Dynamic Islands.
- Spadek zarówno satysfakcji jak i wykorzystania webpacka może sugerować, że technologia ta powoli zaczyna ustępować już miejsca młodszej technologii. Natomiast wynik 98% satysfakcji z Vite robi piorunujące wrażenie i pozwala wnioskować kto zajmie miejsce wysłużonego webpacka.
- Tylko 27% respondentów poświęca więcej swojego czasu na pisanie w JavaScript niż w TypeScript i tylko 11% respondentó nie korzysta z TypeScript wcale.
3. JavaScript Raising Stars
Po przeczytaniu State of JS 2022 wciąż mało Wam statystyk i podsumowań? W minionym tygodniu opublikowany został również raport 2022 JavaScript Rising Stars, który zestawia ze sobą przyrost GitHubowych gwiazdek różnych narzędzi związanych z JavaScript. Długo możnaby dystkutować, czy gwiazdki na GithHubie są dobrym źródłem danych o popularności bibliotek, ale w raporcie na pewno natraficie na kilka ciekawych narzędzi, które uciekły poza Waszymi radarami. Dodatkowo każda sekcja okraszona komentarzem ekspertów, którzy zazwyczaj mają szerszy pogląd na całą sytuację.
Dla wszystkich, którzy nie mają czasu, siły lub ochoty na przeczytanie raportu, uchylę tajemnicy. W tym roku zestawienie zostało zdominowane przez Bun – szybszą alternatywę typu drop-in dla Node.js i Deno – która miała premierę w połowie tego roku. Zespół rozwijający to środowisko uruchomieniowe zebrał już 7M$ na jego dalszy rozwój, także naprawdę warto obserwować, co będzie działo się z nim w przyszłości.
Zainstaluj teraz i czytaj tylko dobre teksty!
Bonus: JavaScript Wrapped 2022
Jeśli jakimś cudem jeszcze nie dość Wam podsumowań, to w minionym tygodniu popełniłem całkiem obszerne podsumowanie minionego roku z perspektywy redaktora Frontend Weekly. Jeśli jeszcze go nie czytaliście, a chcielibyście przekonać się co mogło Was ominąć w 2022 roku, to gorąco zapraszam do lektury.