DEV Community

Cover image for Angular Control Flow: the complete guide
Davide Passafaro
Davide Passafaro

Posted on • Updated on • Originally published at codemotion.com

Angular Control Flow: the complete guide

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 {}
Enter fullscreen mode Exit fullscreen mode

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*
}
Enter fullscreen mode Exit fullscreen mode

@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>
Enter fullscreen mode Exit fullscreen mode

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*
}
Enter fullscreen mode Exit fullscreen mode

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*
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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*
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
itemById(index, item) {
    return item.id;
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
jangelodev profile image
João Angelo

Hi Davide Passafaro,
Your tips are very useful
Thanks for sharing

Collapse
 
davidepassafaro profile image
Davide Passafaro

Thanks for the kind feedback 😁