Angular v17 was released some months 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.
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 {}
This simple directive has indeed some cons:
- The syntax is not intuitive and to much 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*
}
@if
offers you a cleaner syntax, the conditional blocks stand out immediately while reading the template, allowing to wrap groups of elements inside curly brackets an most of all: no more modules or directives to import.
@else
and @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>
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*
}
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*
}
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>
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*
}
}
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>
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 optional parameter, used to provide to 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>
itemById(index, item) {
return item.id;
}
Thanks to this Angular is able to optimize performances 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>
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 that requires to provide 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 has 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>
}
Awesome indeed 🤩
Thanks for reading so far 🙏
As you could see the new control flow offers a fresh new way to write complex templates condition in our Angular applications, which brings performance improvements and more maintainable code.
If you want to try it already I suggest you to 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
Thanks for reading, I’d like to have your feedback so please leave a comment, like or follow. 👏
And if you really liked it please follow me on LinkedIn. 👋
Top comments (2)
Hi Davide Passafaro,
Your tips are very useful
Thanks for sharing
Thanks for the kind feedback 😁