Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Control flow: @if, @for, @switch

Control flow blocks decide which part of a template to show and how many times to repeat it. They start with the @ sign and are built into the template language, so you do not need to import anything.

@if

The block is rendered only when the condition is true:

<!-- src/app/todo-list/todo-list.html -->
@if (todos().length === 0) {
  <p>No tasks yet.</p>
}

While the list is empty, the page shows "No tasks yet.". As soon as a task appears, the paragraph is removed from the markup completely.

@else and @else if

<!-- src/app/todo-list/todo-list.html -->
@if (left() === 0) {
  <p>All done!</p>
} @else if (left() === 1) {
  <p>One task left.</p>
} @else {
  <p>Tasks left: {{ left() }}</p>
}

The first branch with a true condition runs, just like an ordinary JavaScript if.

@for

@for repeats a block for every array element. The track expression is required: it tells Angular which field identifies an element.

<!-- src/app/todo-list/todo-list.html -->
<ul>
  @for (todo of todos(); track todo.id) {
    <li [class.done]="todo.done">{{ todo.title }}</li>
  }
</ul>

Each task becomes a list item, and completed ones get the done class. Thanks to track, when a task is added Angular creates one new li instead of redrawing the whole list.

@empty and helper variables

The @empty block is shown when the array is empty. Inside @for the variables $index, $count, $first, $last, $even and $odd are available:

<!-- src/app/todo-list/todo-list.html -->
<ul>
  @for (todo of todos(); track todo.id) {
    <li>{{ $index + 1 }}. {{ todo.title }}</li>
  } @empty {
    <li>No tasks yet.</li>
  }
</ul>

Tasks are shown with numbers: "1. Buy bread", "2. Call Oleg".

@switch

Useful when you pick one option out of several by value. Add a filter to the class:

// src/app/todo-list/todo-list.ts
filter = signal<'all' | 'active' | 'done'>('all');
<!-- src/app/todo-list/todo-list.html -->
@switch (filter()) {
  @case ('active') {
    <h3>Active tasks</h3>
  }
  @case ('done') {
    <h3>Completed tasks</h3>
  }
  @default {
    <h3>All tasks</h3>
  }
}

The heading changes together with the filter signal. The @default block runs when no @case matches.

The old syntax

Before Angular 17, the directives *ngIf, *ngFor and [ngSwitch] were used instead of blocks. They still work, but in new projects write @if, @for and @switch — they need no imports and are faster.

Common mistake

Writing track $index for a list that changes: when a task is removed from the middle, the indexes shift, and Angular redraws every element after it — typed text and focus are lost. Track elements by a unique field, for example track todo.id.

Thanks for reading!

Donate to the site — we work hard for you.