Angular @let keyword | Declare variable in HTML Template
3 min read
·
23 hours ago- To be honest, when I first saw this feature, I said, ‘Why should I use this? I can simply define a variable in the component class and bind it to the template.
- But I was forgetting some of the problems I was experiencing. 🙂
- You are probably thinking the same way, but I will change your mind in this blog post. Please keep reading! 😀
What is the Purpose
This feature was introduced in 18.1.0, and the main idea is to simplify template logic and improve readability, especially in deeply nested objects, repetitive code blocks, and asynchronous (async pipe) operations.
I will provide real-life scenarios and best practices as much as I can. But before diving in, let’s check the syntax.
SYNTAX
All you have to do is use the @let decorator and define a variable just as you would in JavaScript.
- Similar to
let
in JavaScript, its value can be any Angular expression. - You can’t modify it because it is read-only.
- The expression will be re-evaluated every time the template is executed.
- It is block scoped.
Use Cases for the let Syntax
Basic Usage
@let name = state().name;<p> {{ name }}</p>
Minimize duplicate expressions
@let address = state().personal.location.address;<p> {{ address.street }} {{ address.country }} {{ address.city }}</p>
Avoiding Multiple Subscriptions with the Async Pipe
- I am sure you have encountered the problem where you need to use the async pipe for the same observable multiple times, resulting in multiple subscriptions. That’s not what we want.
- With this new feature, we can solve this problem easily.
<first-component [name]="(myObservable$ | async).name"><second-component [surname]="(myObservable$ | async).surname">// SOLUTION@let myObservable = myObservable$ | async;<first-component [name]="myObservable.name"><second-component [surname]="myObservable.surname">
- The Angular team recommends using
let
instead ofas
, especially if you’ve moved to signals.
Signal Type Narrowing in Templates
- If you are using signals, you are probably aware of the annoying lack of type narrowing in signals. See the example below.
@if(state()){ {{ state().name }} // Object is possibly 'undefined'.}// SOLUTIONS@let state = state();@if(state){ {{ state.name }}}
Effective Use of NgClass Assignments
<div [ngClass]="{ success: doorState()?.options?.isSuccess, error: doorState()?.options?.isError, opening: doorState()?.options?.isOpening, }"> Door Related Stuffs</div>// CLEANER WAY@let door = doorState()?.options;@let doorClass = { success: door?.isSuccess, error: door?.isError, opening: door?.isOpening };<div [ngClass]="doorClass">Door Related Stuffs</div>
Performing Calculations and Calling Methods
@let totalPrice = book.price * qty;<div class="total-price"> {{ totalPrice }}</div>// Or you can use methods from your component class@let totalPrice = getTotalPrice(book, qty);<div class="total-price"> {{ totalPrice }}</div>
Bind template variable to local template variable with @let
<input #myInput type="text" />@let inputValue = myInput.value;<p>{{ inputValue }}</p>
I think this new feature solves some of the problems and improves the readability of our code. What do you think about it?
If you have any advice or ideas about it, please feel free to share them.