After a long discussion, Angular finally introduced a new Control Flow Syntax in version 17.
This new syntax allows developers to apply conditional logic and loops directly in the template, without the need for structural directives like *ngIf
and *ngFor
.
It’s simply closer to the syntax used in languages such as Typescript and JavaScript, making it easier to read and understand.
The new @for
uses a new diffing algorithm that improves by 90% the runtime performances (read the v17 announcement for more details).
@if (condition) {
<div>Content</div>
}
@if (condition) {
<div>Content</div>
} @else if (otherCondition) {
<div>Other content</div>
} @else {
<div>Default content</div>
}
@if (items$ | async; as items) {
<div>{{ items }}</div>
}
as
is used to assign the value of the observable to a variable. If the observable is a HttpClient call,
it’s important to use the async
pipe only once, otherwise you get multiple network calls.
items
is only available within the @if
scope.
@let items = items$ | async;
@if (items) {
<div>{{ items }}</div>
}
let
is available since Angular 18.2.
@for (product of products; track product.id) {
<div>{{ product.name }}</div>
}
track
is now mandatory and it plays a key role into the DOM rendering of newly added, updated or removed items.
Variable | Meaning |
---|---|
$count | Number of items in a collection iterated over |
$index | Index of the current row |
$first | Whether the current row is the first row |
$last | Whether the current row is the last row |
$even | Whether the current row index is even |
$odd | Whether the current row index is odd |
It’s no longer necessary to wrap a @for
loop with an @if
to check if the array is empty.
With the new @empty
syntax, you can directly check if the array is empty and show a proper message.
@for (product of products; track product.id) {
<div>{{ product.name }}</div>
} @empty {
<div>No products</div>
}
@switch (product.status) {
@case ('unavailable') {
<strong>Unvailable</strong>
}
@case ('inStock') {
<strong>In Stock</strong>
}
@default {
<strong>-</strong>
}
}
To showcase a real world example, let’s consider a simple list of products.
@if (loading) {
<div>Loading...</div>
} @else if (error) {
<div>Error</div>
} @else if (products.length) {
@for (product of products; track product.id) {
<div>{{ product.name }}</div>
} @empty {
<div>No products</div>
}
}
This example, of course, could be improved by introducing a status
variable and using the switch case.
To migrate to the new Control Flow Syntax, you can use the Angular CLI.
There is a new schematic that will migrate the code for you.
ng generate @angular/core:control-flow
Currently, there is no official plan to deprecate the old syntax but, given the improvements in the code redability and performances, it’s likely that the old directives will be deprecated soon.
The new Control Flow Syntax is a big step forward for Angular, but it’s important to consider some aspects before migrating.
First, run the migration on a branch and test that the application compiles. If you have a test suite, make sure to run it to check that the application behaves as expected.
For sure, you must consider to use the new syntax for new features.