FrancescoDonzello
Angular Share on Twitter

@if, @for: How to migrate to the new Control Flow Syntax

4 minutes read
#articles#angular

Introduction

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.

Good bye structural directives

The new @for uses a new diffing algorithm that improves by 90% the runtime performances (read the v17 announcement for more details).

@if

@if (condition) {
  <div>Content</div>
}

@if, @else

@if (condition) {
  <div>Content</div>
} @else if (otherCondition) {
  <div>Other content</div>
} @else {
  <div>Default content</div>
}

@if, as

@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.

Scope

items is only available within the @if scope.

@let

@let items = items$ | async;
@if (items) {
  <div>{{ items }}</div>
}

let is available since Angular 18.2.

@for

@for (product of products; track product.id) {
  <div>{{ product.name }}</div>
}

track

track is now mandatory and it plays a key role into the DOM rendering of newly added, updated or removed items.

Contextual Variables

VariableMeaning
$countNumber of items in a collection iterated over
$indexIndex of the current row
$firstWhether the current row is the first row
$lastWhether the current row is the last row
$evenWhether the current row index is even
$oddWhether the current row index is odd

@empy

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

@switch (product.status) {
  @case ('unavailable') {
    <strong>Unvailable</strong>
  }
  @case ('inStock') {
    <strong>In Stock</strong>
  }
  @default {
    <strong>-</strong>
  }
}

A common use case

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.

Migrate the codebase

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

What about the future of *ngIf, *ngFor?

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.

Considerations

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.

← Back to Angular Articles

Was it helpful?

Tell your friends or co-workers.