Exploring Angular’s New @let Syntax: Enhancing Template Variable Declarations
Angular’s evolution continues with exciting new features, including the recently merged @let
syntax, which is now avilalabe in 18.1.0-next.4. These additions offer a streamlined way for developers to declare local variables and manage control flow directly within templates. This feature aims to simplify template logic and improve readability, especially when dealing with asynchronous data and complex conditions. Let's delve into the various use cases and advantages of the new @let
syntax.
Understanding the @let Syntax
The @let
syntax allows developers to declare and use local variables within their Angular templates. Here's a basic example to illustrate its usage:
@let user = user$ | async;
@let greeting = user ? 'Hello, ' + user.name : 'Loading';
<h1>{{ greeting }}</h1>
In this example, user$
is an observable that emits user data. The @let
syntax simplifies the process of handling asynchronous data and conditional rendering.
Use Cases for the let Syntax
Avoiding Falsy Values or Multiple Subscriptions with the Async Pipe
Managing observables and their emitted values in templates can lead to repetitive code and multiple subscriptions. Let’s look at some common issues and how @let
solves them.
First, using the async
pipe within an @if
statement won't work correctly if the observable value is falsy (e.g., 0
or false
):
@if (total$ | async as total) {
<p>{{ total }}</p>
}
Second, using the async
pipe directly in multiple places can lead to multiple subscriptions, which is inefficient and can cause performance issues:
<div>{{ total$ | async }}</div>
<div>{{ total$ | async }}</div>
The solution is to use the @let
syntax to declare a variable for the observable value. This approach ensures a single subscription and handles falsy values correctly.
@let total = total$ | async;
<div>{{ total }}</div>
<div>{{ total }}</div>
Signal Type Narrowing in Templates
One of the most annoying issues with signals is their lack of type narrowing ability within templates. Although Angular is working on a solution for this, we can leverage the @let
feature to address this problem in the meantime:
@let txType = tx().type;
@switch(txType) {
@case('a') {}
@case('b') {}
}
@let address = person()?.address();
@if (address) {
<app-address [address]="address">
}
Inside for Loops
The @let
syntax particularly useful within @for
loops. Developers can create intermediate properties directly in the template, enhancing readability and reducing the need for additional component logic. For instance:
@for (user of users(); track user.id) {
@let address = user.address;
<div>
<h3>User: {{ user.name }}</h3>
<div>
<p>Street: {{ address.street }}</p>
<p>City: {{ address.city }}</p>
<p>Zipcode: {{ address.zipcode }}</p>
</div>
@for (order of user.orders) {
<div>
<h4>Order: {{ order.id }}</h4>
<p>Product: {{ order.productName }}</p>
<p>Quantity: {{ order.quantity }}</p>
</div>
}
</div>
}
Using Pipes Multiple Times
When using an expensive pipe multiple times in a template, it often necessitates creating a property in the component to store the transformed data. With the @let
syntax, you can declare a transformed variable once and reuse it, reducing the computational load and keeping your template clean.
@let expensiveResult = someData | expensivePipe;
<p>{{ expensiveResult }}</p>
<p>{{ expensiveResult }}</p>
Conclusion
The @let
syntax in Angular, combined with the new control flow features like @if
and @for
, offers a significant improvement for template variable declarations and control flow management. While some may argue for the continued use of signals for state management, the @let
syntax provides an elegant solution for handling local variables within templates. By addressing common challenges such as managing falsy values, avoiding multiple subscriptions, and reducing repetitive code, this new feature is poised to enhance the development experience for Angular developers.
Follow me on Medium or Twitter to read more about Angular and JS!