Wraz z nadejściem kolejnej wersji Angulara (wersja 17.) w listopadzie 2023 roku, pojawiły się nowe funkcjonalności do pracy z widokami HTML, które są bardziej czytelne od wcześniejszych. Mam tu na myśli często używane *ngIf, *ngFor, a także *ngSwitch, będące podstawowymi, wbudowanymi dyrektywami od początku istnienia angularowego świata.
W artykule, na podstawie przykładów, opiszę i porównam:
- jak wyglądało używanie wcześniejszych dyrektyw;
- w jaki sposób możemy skorzystać z istniejącego nowego rozwiązania;
- jakie dodatkowe elementy wprowadza nowy Control Flow, pozwalając na łatwiejsze budowanie aplikacji np. @empty;
łatwiejszy i wygodniejszy sposób na dodawanie trackBy do @for.
Pierwsze kroki
Pamiętajmy, aby zainstalować Angulara w wersji 17. Aby to zrobić, należy użyć terminala, wykonując następujące komendy:
npm install -g @angular/cli@latest
ng new my-first-project–angular-version-17
Po utworzeniu i uruchomieniu nowego projektu, możemy korzystać z nowoczesnych dobrodziejstw.
Cały control flow nowego podejścia zaprezentuję na przykładzie prostego widoku, na którym mamy informację o nowym pracowniku.
Po naciśnięciu przycisku „More information”, zostaną pokazane dodatkowe informacje na temat pracownika, a wszystko oparte jest na 3 dyrektywach:
- *ngIf,
- *ngFor,
- *ngSwitch.
No to zaczynajmy 🙂
*ngIf / @if @else
Na początku przedstawię najprostszy sposób wyświetlania informacji w aplikacji, czyli naszą dyrektywę *ngIf. Służy ona do pokazywania lub ukrywania informacji w templatce.
W naszym przykładzie na początku ukrywamy, a później pokazujemy informację o wybranym pracowniku:
<!-- Old Syntax -->
<div *ngIf="isInfoVisible; else noInformation">
<p class="age"><b>Age:</b> {{user.age}}</p>
<p><b>Job position:</b> {{user.jobPosition}}</p>
<p class="header-skill">Skills:</p>
<ul class="skills">
<li *ngFor="let skill of user.skills">{{skill}}</li>
</ul>
</div>
<ng-template #noInformation>
<p class="no-information">
Show more information about {{user.fName}} {{user.lName}}
</p>
</ng-template>
Nowa składnia jest całkiem przyjazna dla developera, gdyż wszystko zamyka się w konstrukcji @if @else oraz nawiasach klamrowych.
Ponadto, pozbywamy się dodatkowego kodu jak ng-template, a cała logika zaczyna być trochę oddzielona od tagów HTML.
Nic nie stoi na przeszkodzie, aby dodać więcej warunków np.: @if @else if @else.
<!-- New Syntax -->
@if (isInfoVisible) {
<div>
<p class="age"><b>Age:</b> {{user.age}}</p>
<p><b>Job position:</b> {{user.jobPosition}}</p>
<p class="header-skill">Skills:</p>
<ul class="skills">
<li *ngFor="let skill of user.skills">{{skill}}</li>
</ul>
</div>
} @else {
<p class="no-information">
Show more information about {{user.fName}} {{user.lName}}
</p>
}
Lista umiejętności pracownika, czyli *ngFor vs. @for @empty
Jeżeli chodzi o wyświetlanie list oraz iterację po każdym elemencie, to tutaj bardzo przydatną dyrektywą jest *ngFor, która pozwala na wyświetlanie dynamicznych informacji.
W dotychczasowym podejściu wykonywaliśmy pętlę *ngFor bezpośrednio na iterowany elemencie.
<!-- Old Syntax -->
<p class="header-skill">Skills:</p>
<ul class="skills">
<li *ngFor="let skill of user.skills">{{skill}}</li>
</ul>
W nowym podejściu została wprowadzona pętla @for. Istotną zmianą, która bardzo dobrze wpływa na performance aplikacji, jest fakt, że w nowej składni musimy przekazać wymaganą metodę trackBy. Przypomnę, że przy *ngFor tworzyliśmy taką metodą w pliku *.ts, i nie było to wymagane, zależało od front-end developera.
<!-- New Syntax -->
<p class="header-skill">Skills:</p>
<ul class="skills">
@for (skill of user.skills; track $index) {
<li>{{skill}}</li>
} @empty {
<li>No skills on the list</li>
}
</ul>
Dodatkową zaletą nowego podejścia jest @empty, która pokazuje informację, jeżeli nasza lista jest pusta. Wcześniej nie mieliśmy takiej możliwości i trzeba było wykonać kolejne operacje razem z *ngIf, aby uzyskać podobny efekt.
Nowa odsłona *ngSwitch
Ostatnią dyrektywą i nową składnią, którą chciałbym przedstawić jest *ngSwitch oraz @Switch @case. Służy ona do wyświetlania wielu informacji w zależności od spełnionego warunku. W naszym widoku mamy doświadczenie wybranego pracownika.
<!-- Old Syntax -->
<p>
<b>Experience: </b>
<ng-container [ngSwitch]="user.experience">
<span *ngSwitchCase="experience.Small">
Junior Software Engineer
</span>
<span *ngSwitchCase="experience.Medium">
Regular Software Engineer
</span>
<span *ngSwitchCase="experience.Big"
>Senior Software Engineer
</span>
<span *ngSwitchDefault>Intern</span>
</ng-container>
</p>
W nowej składni, podobnie jak wcześniej, mamy oddzielenie logiki od tagów HTML, przez co kod jest znacznie czytelniejszy. Jeżeli żadna wartość nie pasuje do naszego, możemy skorzystać z wersji @default, podobnie jak było w starej składni *ngSwitchDefault.
<!-- New Syntax -->
<p>
<b>Experience: </b>
@switch (user.experience) {
@case (experience.Small) {
<span>Junior Software Engineer</span>
}
@case (experience.Medium) {
<span>Regular Software Engineer</span>
}
@case (experience.Big) {
<span>Senior Software Engineer</span>
}
@default { <span>Intern</span> }
}
</p>
Migracja
Jeżeli pracujemy w projekcie z niższą wersją, a następnie chcemy zaktualizować go do wersji 17-tej, możemy skorzystać także z dodatkowej instrukcji, która pozwoli nam na zmianę dotychczasowej składni na nowy control-flow. W tym celu wystarczy użyć instrukcji w terminalu:
ng g @angular/core:control-flow-migration
Składnia z @, czy jednak powinniśmy zostać przy dyrektywach?
Team Angulara, wprowadzając nową składnię dla widoków, nie zrezygnował z istniejących dyrektyw. A więc tworząc nowy projekt w wersji 17. Angulara, możemy korzystać z obu opcji, w zależności od naszych upodobań. Którą wersję wybrać i która jest lepsza? Odpowiedź pozostawiam do indywidualnej oceny każdego front-end developera albo do zespołu, który pracują z aplikacją 😊
Nowa składnia ma za zadanie oddzielić w widoczny sposób składnię logiki od elementów HTML. Drugim istotnym powodem jest wprowadzenie komponentów opartych na sygnałach. Mają one nową detekcję zmian bez zone.js – w Angular jest to NgZone. Obecne dyrektywy oparte są na zone.js i mogłyby nie zadziałać w komponentach bez zone.
Podsumowanie
Ja osobiście polubiłem nową składnię, ponieważ jest dla mnie czytelniejsza oraz, jak wspomniałem, oddziela logikę od tagów HTML. Ponadto, dodaje dodatkowe funkcjonalności jak @empty czy wymuszony trackBy. Z chęcią będę z niej korzystał przy kolejnych projektach.
Cały kod, który został przedstawiony w tym artykule, możecie pobrać z mojego repozytorium i sprawdzić, jak to działa. Zachęcam do instalowania nowej wersji Angulara lub podbicia paczek do wersji 17. i korzystania z nowości, które powinny nam ułatwić, a czasami także przyspieszyć, pracę nad rozwijanymi aplikacjami.
***
***
Jeśli interesuje Cię Angular, zajrzyj również do innych artykułów naszych ekspertów.
Fajna zmiana w dobrym kierunku. Zawsze mnie irytowały dyrektywy w templatce. Było to dużo mniej czytelne.
Bardzo wartościowy artykuł. Dziękuje za praktyczne wskazówki!
Połączenie Razora i składni C# w czystej postaci wyszedł z tego widok z Blazora albo jak kto woli MVC View lub Razor Pages w czystej postaci. Dla mnie, programisty .NET bardzo intuicyjna składnia. Przesiadka w tym aspekcie nie wymaga nauki. Ciekawe czy się sugerowali przy implementacji rozwiązaniami z .NET