Niedawne ogłoszenie bloku @let w Angularze wywołało znaczną debatę w społeczności deweloperów. Podczas gdy niektórzy postrzegają go jako cenne uzupełnienie, inni uważają, że to zbędna komplikacja.
Argumenty za @let
Zwolennicy twierdzą, że @let upraszcza logikę szablonów, umożliwiając deklarowanie zmiennych bezpośrednio w szablonie, unikając problemów z wartościami “falsy” oraz poprawiając czytelność. Pozwala to na bardziej przejrzysty i czystszy kod, zwłaszcza przy pracy ze złożonymi warunkami i asynchronicznymi danymi.
Problem z wartościami “falsy”
Wcześniej, aby zadeklarować zmienną w szablonie Angulara, deweloperzy często używali dyrektywy ngIf ze słowem kluczowym as. Jednak ta metoda miała ograniczenie: wartości “falsy”, takie jak 0, puste stringi (""), null, undefined i false, uniemożliwiały renderowanie zawartości. Na przykład:
<div *ngIf="userName$ | async as userName">
<h1>Witaj, {{ userName }}</h1>
</div>
Jeśli userName byłby pustym ciągiem, nic by się nie wyświetliło. Blok @let rozwiązuje ten problem:
<div>
@let userName = (userName$ | async) ?? 'Gość';
<h1>Witaj, {{ userName }}</h1>
</div>
Złożone szablony z dynamicznymi kolumnami
Kolejnym dobrym zastosowaniem tej funkcji są złożone szablony HTML, w których definicje kolumn i wartości wynikają ze złożonych konfiguracji. Każdy, kto pracował nad aplikacją biznesową z dużymi tabelami, zna te wyzwania. @let może znacząco uprościć zarządzanie tymi tabelami.
Z @let:
<table mat-table [dataSource]="dataSource">
@for (columnDef of columnDefs) {
@let property = columnDef.propertyName;
<ng-container [matColumnDef]="columnDef.name">
<th mat-header-cell *matHeaderCellDef>{{ columnDef.header }}</th>
<td mat-cell *matCellDef="let element">
@let cellValue = element[property];
<ng-container *ngIf="columnDef.cellType === 'link'; else plainCell">
<a [routerLink]="cellValue?.routerLink">{{ cellValue?.value }}</a>
</ng-container>
<ng-template #plainCell>{{ cellValue }}</ng-template>
</td>
</ng-container>
}
</table>
Bez @let:
<table mat-table [dataSource]="dataSource">
<ng-container *ngFor="let columnDef of columnDefs">
<ng-container [matColumnDef]="columnDef.name">
<th mat-header-cell *matHeaderCellDef>{{ columnDef.header }}</th>
<td mat-cell *matCellDef="let element">
<ng-container *ngIf="columnDef.cellType === 'link'; else plainCell">
<a [routerLink]="element[columnDef.propertyName]?.routerLink">{{ element[columnDef.propertyName]?.value }}</a>
</ng-container>
<ng-template #plainCell>{{ element[columnDef.propertyName] }}</ng-template>
</td>
</ng-container>
</ng-container>
</table>
Argumenty przeciw @let
Krytycy twierdzą, że blok @let wprowadza zbędną złożoność i może prowadzić do zamieszania. Oto kilka przykładów ilustrujących ich obawy:
- Zwiększone obciążenie poznawcze: Wprowadzenie @let dodaje kolejny koncept, którego deweloperzy muszą się nauczyć i zrozumieć. Może to zwiększyć obciążenie poznawcze, zwłaszcza dla nowych deweloperów, którzy już starają się pojąć rozbudowane funkcje Angulara.
- Potencjalne nadużycia: Istnieje ryzyko, że deweloperzy będą nadużywać lub źle wykorzystywać @let, prowadząc do szablonów, które są trudniejsze do odczytu i utrzymania. Na przykład, zagnieżdżanie wielu bloków @let w złożonych szablonach może sprawić, że kod będzie mniej przejrzysty. Podczas recenzji kodu konieczna będzie dodatkowa uwaga, aby upewnić się, że nie tworzy się zbędnych zmiennych szablonowych.
- Istniejące rozwiązania są wystarczające: Krytycy uważają, że istniejące funkcje Angulara, zwłaszcza możliwość konwersji observabli na sygnały, są wystarczające do obsługi większości przypadków użycia. Twierdzą, że wprowadzenie @let nie przynosi znaczących korzyści w porównaniu z tymi istniejącymi rozwiązaniami.
Przykład ilustrujący złożoność
Rozważmy scenariusz, w którym w szablonie deklarowanych jest wiele zmiennych. Użycie @let może sprawić, że szablon stanie się bardziej zagracony i trudniejszy do śledzenia:
<div>
@let firstName = user?.firstName;
@let lastName = user?.lastName;
@let fullName = `${firstName} ${lastName}`;
<p>{{ fullName }}</p>
@if (user?.address) {
@let street = user.address.street;
@let city = user.address.city;
<p>{{ street }}, {{ city }}</p>
}
</div>
Choć ten przykład pokazuje potencjalną wygodę @let, ilustruje również, jak wielokrotne deklaracje @let mogą prowadzić do bałaganu w szablonie.
Moja opinia
Prawdopodobnie jestem gdzieś pośrodku. Na samym początku nie widziałem wielu przypadków użycia tej funkcji, zwłaszcza teraz, gdy mogę tworzyć sygnały z observabli i używać ich nie tylko w moim szablonie, ale także w moich plikach .ts, gdy tylko potrzebuję. Centralizuje to źródło moich wartości, pozwalając na ich ponowne użycie bez dodatkowej złożoności. Podczas refaktoryzacji mojego kodu, jeśli napotkam przypadek z *ngIf i składnią "as" z observablem, wolałbym użyć sygnału zamiast tworzenia zmiennej szablonu z @let.
Jednak przykład z tabelą (dzięki @skorupka_k za zwrócenie na to uwagi) przekonał mnie, że złożone tabele są bardzo dobrym miejscem do wykorzystania tej funkcji. Kiedykolwiek będę z nimi pracował ponownie, zdecydowanie rozważę użycie @let.
Ogólnie rzecz biorąc, wolałbym unikać jej używania i zachowałbym szczególną ostrożność podczas przeglądów kodu, aby upewnić się, że jest używana tylko wtedy, gdy jest to konieczne, ale mimo to lepiej mieć ją w Angularze niż nie!
Podsumowanie
Blok @let w Angularze to kontrowersyjna funkcja, przez jednych chwalona jako przełomowa, przez innych odrzucana jako zbędna. Czy jest hitem czy kitem, zależy ostatecznie od tego, jak deweloperzy zdecydują się ją zintegrować w swój workflow.
Jakie są Twoje myśli? Czy funkcjonalność @let to mile widziane uzupełnienie, czy zbędna komplikacja? Podziel się swoimi opiniami w komentarzach poniżej.
Źródło: [justangular.com](https://justangular.com/blog/template-local-variables-with-let-in-angular)