Перейти к содержанию

Управление потоком

📅 30.09.2026

Шаблоны Angular поддерживают блоки управления потоком: через них элементы условно показывают, скрывают и повторяют.

Условный показ содержимого через @if, @else if и @else

Блок @if показывает своё содержимое, когда выражение условия истинно:

1
2
3
@if (a > b) {
  <p>{{ a }} is greater than {{ b }}</p>
}

Альтернативное содержимое задают любым числом блоков @else if и одним блоком @else.

1
2
3
4
5
6
7
@if (a > b) {
  {{ a }} is greater than {{ b }}
} @else if (b > a) {
  {{ a }} is less than {{ b }}
} @else {
  {{ a }} is equal to {{ b }}
}

Ссылка на результат условного выражения

В @if результат условного выражения можно сохранить в переменную и переиспользовать внутри блока.

1
2
3
@if (user.profile.settings.startDate; as startDate) {
  {{ startDate }}
}

Так удобнее ссылаться на длинные выражения: в шаблоне их проще читать и сопровождать.

Повтор содержимого блоком @for

Блок @for проходит по коллекции и многократно отрисовывает содержимое блока. Коллекция может быть любым итерируемым значением JavaScript, но для значений Array у Angular есть дополнительные оптимизации производительности.

Обычный цикл @for выглядит так:

1
2
3
@for (item of items; track item.id) {
  {{ item.name }}
}

Блок @for в Angular не поддерживает операторы, меняющие ход выполнения, вроде continue или break в JavaScript.

Зачем в блоках @for нужен track?

Выражение track позволяет Angular удерживать связь между данными и узлами DOM на странице. За счёт этого при изменении данных Angular выполняет только необходимый минимум операций с DOM.

Грамотный track заметно ускоряет отрисовку приложения при обходе коллекций данных.

В выражении track выбирайте свойство, которое однозначно отличает каждый элемент. Если в модели данных есть такое поле, обычно id или uuid, берите его. Если такого поля нет, стоит его добавить.

Для статических коллекций, которые никогда не меняются, можно использовать $index: тогда Angular отслеживает каждый элемент по его индексу в коллекции.

Если другого варианта нет, ключом отслеживания может быть сам элемент. Тогда Angular отслеживает элемент по идентичности ссылки через оператор тройного равенства (===). Этого варианта лучше избегать: обновления отрисовки становятся заметно медленнее, потому что Angular не может сопоставить, какой элемент данных какому узлу DOM соответствует.

1
2
3
@for (item of items; track item) {
  {{ item.name }}
}

В отличие от *ngFor, блок @for в первую очередь переиспользует представление. Если отслеживаемое свойство изменилось, а ссылка на объект осталась той же, Angular обновляет привязки представления (включая входы компонента), а не уничтожает и не создаёт элемент заново.

Контекстные переменные в блоках @for

Внутри блоков @for всегда доступны несколько неявных переменных:

Переменная Значение
$count Число элементов в обходимой коллекции
$index Индекс текущей строки
$first Является ли текущая строка первой
$last Является ли текущая строка последней
$even Чётный ли индекс текущей строки
$odd Нечётный ли индекс текущей строки

Эти переменные всегда доступны под такими именами, но их можно переименовать через сегмент let:

1
2
3
@for (item of items; track item.id; let idx = $index, e = $even) {
  <p>Item #{{ idx }}: {{ item.name }}</p>
}

Псевдонимы удобны при вложенных блоках @for: из внутреннего блока можно прочитать переменные внешнего.

Запасной вариант для @for через блок @empty

Сразу после содержимого блока @for можно добавить секцию @empty. Содержимое блока @empty показывается, когда элементов нет:

1
2
3
4
5
@for (item of items; track item.name) {
  <li>{{ item.name }}</li>
} @empty {
  <li>There are no items.</li>
}

Условный показ содержимого блоком @switch

Блок @if закрывает большинство случаев, а @switch даёт другой синтаксис условной отрисовки данных. Он близок к оператору switch в JavaScript.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@switch (userPermissions) {
  @case ('admin') {
    <app-admin-dashboard />
  }
  @case ('reviewer')
  @case ('editor') {
    <app-editor-dashboard />
  }
  @default {
    <app-viewer-dashboard />
  }
}

Значение условного выражения сравнивается с выражением ветки оператором тройного равенства (===).

У @switch нет проваливания в следующую ветку, поэтому внутри блока не нужен аналог break или return.

Несколько условий для одного блока задают подряд идущими операторами @case.

Блок @default необязателен. Его содержимое показывается, если ни одно из предыдущих выражений @case не совпало со значением switch.

Если ни один @case не совпал с выражением и блока @default нет, ничего не показывается.

Исчерпывающая проверка типов

@switch поддерживает исчерпывающую проверку типов: на этапе компиляции Angular проверяет, что обработаны все возможные значения типа-объединения.

Запись @default never; явно говорит, что оставшихся веток быть не должно. Если тип-объединение позже расширят и новая ветка не покрыта @case, проверка типов шаблона Angular сообщит об ошибке — пропущенную ветку видно сразу.

Проверка исчерпываемости опирается на сужение типов TypeScript, а оно работает только с переменными. Она не сработает, если условие switch — вызов функции или сигнал (например, @switch (state())). Обходной путь — присвоить сигнал переменной @let, например: @let mySignal = this.mySignal().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Component({
  template: `
    @switch (state) {
      @case ('loggedOut') {
        <button>Login</button>
      }

      @case ('loggedIn') {
        <p>Welcome back!</p>
      }

      @default never;
      <!-- throws because @case ('loading') is missing -->
    }
  `,
})
export class AppComponent {
  state: 'loggedOut' | 'loading' | 'loggedIn' = 'loggedOut';
}

Если переключаемое выражение вложено в объединение, выражение для проверки исчерпываемости нужно указать явно.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@Component({
  template: `
    @switch (state.mode) {
      @case ('show') {
        {{ state.menu }};
      }
      @case ('hide') {}
      @default never(state);
    }
  `,
})
export class App {
  state!: {mode: 'hide'} | {mode: 'show'; menu: number};
}

Источник: https://angular.dev/guide/templates/control-flow

Комментарии