Angular v17 was released some days ago with a ton of new features, a brand-new logo and the new blog angular.dev.
In this article, I will dive into the new control flow, which will make you forget about directives like ngIf, ngSwitch and ngFor thanks to a new syntax to write if, if/else and switch statements and the for loop in our template.
⚠️ FIRST ALERT: the new control flow is still in developer preview ⚠️
But now let’s start our journey learning about the most fundamental condition: @if.
@if condition
In order to show a block of template based on a condition it has always been used the directive ngIf:
@Component({
standalone: true,
template: `<div *ngIf="condition"> ... </div>`,
imports: [NgIf]
})
export class MyComponent {}
Code language: TypeScript (typescript)
This simple directive has indeed some cons:
- The syntax is not intuitive and very framework-related;
- It’s not possible to apply it to groups of elements, to do so you need to wrap the elements inside a container or ng-template/ng-container;
- You need to import CommonModule or the directive NgIf in your modules.
The new @if condition lets you obtain the same result:
@if (condition) {
* your content *
}
Code language: HTML, XML (xml)
@if offers you a cleaner syntax, the conditional blocks stand out immediately while reading the template, allowing you to wrap groups of elements inside curly brackets and most of all: no more modules or directives to import.
@else e @else/if
if/else condition were always a pain in Angular:
<div *ngIf="condition; else otherTemplate">
* your content *
</div>
<ng-template #otherTemplate>
* your other content *
</ng-template>
Code language: HTML, XML (xml)
The need of an ng-template combined with a template variable to provide at ngIf directive a backup block was always tricky and not very immediate.
And here is where @if helps the most, in fact, it can be extended using @else:
@if (condition) {
* your content *
} @else {
* your other content *
}
Code language: HTML, XML (xml)
But it’s not just that, @if can be extended even more using @else/if:
@if (condition) {
* your content *
} @else if (secondCondition) {
* your second content *
} @else {
* your other content *
}
Code language: HTML, XML (xml)
Thanks to this you can create templates with complex conditions writing clean and intuitive code. Easy to read, easy to maintain.
Now, let’s proceed with @switch.
@switch condition
Up until today, the ngSwitch directive was used to show a certain block of template in a list based on a switch condition:
<div [ngSwitch]="condition">
<div *ngSwitchCase="value1">* content block value1 *</div>
<div *ngSwitchCase="value2">* content block value2 *</div>
<div *ngSwitchDefault>* default content *</div>
</div>
Code language: HTML, XML (xml)
Similarly to @if there is now a brand new @switch condition:
@switch(condition) {
@case ('value1') {
* content block value1 *
} @case ('value2') {
* content block value2 *
} @default {
* default content *
}
}
Code language: HTML, XML (xml)
In this case, you can obtain a cleaner syntax and no more imports.
Here we are at the last topic: the @for loop.
@for loop
Showing a list of blocks in a template to represent a list of elements is a key concept of a lot of frameworks, and in Angular this task could be accomplished using the ngFor directive:
<ul>
<li *ngFor="let item of itemList">
{{ item.name }}
</li>
</ul>
Code language: HTML, XML (xml)
This directive allows you to create a customized block for each element of the list, thanks to the ability to use the information of the single element and some local variables provided by the directive itself:
index: number
: index of the element in the list;count: number
: the length of the list;first: boolean
: true if the element is the first of the list;last: boolean
: true if the element is the last of the list;even: boolean
: true if the element index is even;odd: boolean
: true if the element index is odd.
Also, the ngFor directive accepts a function called trackBy as an optional parameter, used to provide Angular a unique identifier for each element of the list (like an id):
<ul>
<li *ngFor="let item of itemList; trackBy: itemById">
{{ item.name }}
</li>
</ul>
Code language: HTML, XML (xml)
itemById(index, item) {
return item.id;
}
Code language: TypeScript (typescript)
Thanks to this Angular is able to optimize the performance when the list is replaced or an element is modified.
So, in order to replace the ngFor directive the new control flow provides @for:
<ul>
@for (item of itemList; track item.id; let idx = $index, e = $even) {
<li>{{ item.name }}</li>
}
</ul>
Code language: HTML, XML (xml)
This new syntax brings some important changes:
- Performance improvements up to 90% as compared to ngFor, thanks to the new algorithm used to implement @for;
- The trackBy function is replaced by the track property which requires providing an expression, easier to write and read than an entire function;
- The track property is required so that we are not going to forget it, as it happens commonly with the trackBy function;
- The local variables have now the prefix $ (e.g.: $index).
Furthermore you can obtain also a cleaner syntax and no more imports.
@empty block
Finally, the new @for loop brings a really useful @placeholder condition that allows to show a block if the list is empty:
@for (item of itemList; track item.id) {
<div>{{ item.name }}</div>
} @placeholder {
<div>No items available</div>
}
Code language: HTML, XML (xml)
Awesome indeed 🤩
Conclusion:
As you can see the new control flow offers a fresh new way to write complex template conditions in our Angular applications, which brings performance improvements and more maintainable code.
⚠️ LAST ALERT: the new control flow is still in developer preview ⚠️
If you want to try it already I suggest you test it on your existing projects. You just have to ng update your app and use the migration command offered by the Angular CLI:
ng generate @angular/core:control-flow
Code language: Bash (bash)
Thanks for reading, I’d like to have your feedback so please feel free to contact me for any. 👋