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-flowCurrently, 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.