Синтаксис Control Flow в Angular 2026: @if, @for, @switch та питання на співбесідах

Повний посібник з блокового синтаксису керування потоком в Angular. Вивчіть @if, @for, @switch з практичними прикладами коду та підготуйтесь до технічних співбесід.

Angular Control Flow Syntax diagram

Синтаксис control flow в Angular (@if, @for, @switch) замінює структурні директиви *ngIf, *ngFor та *ngSwitch, які роками домінували в шаблонах Angular. Представлений в Angular 17, стабільний з Angular 18 та стандартний з Angular 22, цей блоковий синтаксис наближає шаблони до стандартних програмних конструкцій, одночасно забезпечуючи кращі оптимізації продуктивності.

Доступна міграція

Запустіть ng generate @angular/core:control-flow для автоматичної міграції існуючих шаблонів зі структурних директив на новий синтаксис control flow. Обидва синтаксиси можуть співіснувати під час міграції, але ngIf, ngFor та ngSwitch м'яко застарілі з Angular 19.

Чому Angular замінив структурні директиви блоковим синтаксисом

Структурні директиви вимагали імпорту CommonModule, використовували мікро-синтаксис, що відрізнявся від JavaScript, і змушували розробників обгортати вміст у ng-template для складних умов. Новий синтаксис control flow вирішує ці проблеми:

  • Не потребує імпортів: @if, @for та @switch вбудовані у фреймворк
  • JavaScript-подібний синтаксис: умови та цикли читаються як стандартний код
  • Нативна підтримка @else: більше немає потреби в посиланнях на ng-template для гілок else
  • Кращий tree-shaking: невикористані блоки control flow мають нульовий вплив на бандл

Документація Angular рекомендує повністю приймати блоковий синтаксис у всіх нових проектах та поступово мігрувати існуючі кодові бази.

@if: Умовний рендеринг без ng-template

Блок @if умовно рендерить вміст на основі істинного виразу. На відміну від *ngIf, він безпосередньо підтримує гілки @else if та @else:

user-status.component.tstypescript
import { Component, input } from '@angular/core';

@Component({
  selector: 'app-user-status',
  template: `
    @if (user().role === 'admin') {
      <app-admin-panel />
    } @else if (user().role === 'editor') {
      <app-editor-panel />
    } @else {
      <app-viewer-panel />
    }
  `
})
export class UserStatusComponent {
  user = input.required<{ role: string }>();
}

Блок @if також підтримує аліасування змінних за допомогою ключового слова as, яке витягує значення з вкладених виразів:

html
<!-- Уникає повторного доступу до властивостей -->
@if (user().profile?.settings?.theme; as theme) {
  <p>Поточна тема: {{ theme }}</p>
}

Цей патерн особливо корисний при роботі з асинхронними даними або глибоко вкладеними об'єктами, оскільки аліасована змінна визначається лише коли умова істинна.

@for: Ітерація з обов'язковим виразом track

Блок @for ітерує по будь-якому ітерабельному об'єкту JavaScript з оптимізаціями для масивів. На відміну від *ngFor, він вимагає виразу track:

product-list.component.tstypescript
import { Component, input } from '@angular/core';

interface Product {
  id: string;
  name: string;
  price: number;
}

@Component({
  selector: 'app-product-list',
  template: `
    @for (product of products(); track product.id) {
      <div class="product-card">
        <h3>{{ product.name }}</h3>
        <p>{{ product.price | currency }}</p>
      </div>
    } @empty {
      <p>Продукти недоступні</p>
    }
  `
})
export class ProductListComponent {
  products = input.required<Product[]>();
}

Вираз track є обов'язковим, оскільки дозволяє алгоритму порівняння Angular ідентифікувати елементи між циклами виявлення змін. Завдяки цьому фреймворк знає, які елементи DOM слід повторно використати, а які створити заново.

Доступ до контекстних змінних у @for

Блок @for надає кілька контекстних змінних:

html
@for (item of items(); track item.id; let idx = $index, first = $first, last = $last) {
  <div [class.first-item]="first" [class.last-item]="last">
    {{ idx + 1 }}. {{ item.name }}
  </div>
}

Доступні контекстні змінні:

  • $index - поточний індекс (починаючи з 0)
  • $first - чи це перший елемент
  • $last - чи це останній елемент
  • $even - чи індекс парний
  • $odd - чи індекс непарний
  • $count - загальна кількість елементів у колекції

@switch: Множинне розгалуження без ng-container

Блок @switch обробляє сценарії з кількома умовами без необхідності вкладених елементів ng-container:

notification.component.tstypescript
import { Component, input } from '@angular/core';

type NotificationType = 'success' | 'warning' | 'error' | 'info';

@Component({
  selector: 'app-notification',
  template: `
    @switch (type()) {
      @case ('success') {
        <div class="alert alert-success">
          <span class="icon-check"></span>
          <ng-content />
        </div>
      }
      @case ('warning') {
        <div class="alert alert-warning">
          <span class="icon-warning"></span>
          <ng-content />
        </div>
      }
      @case ('error') {
        <div class="alert alert-error">
          <span class="icon-error"></span>
          <ng-content />
        </div>
      }
      @default {
        <div class="alert alert-info">
          <span class="icon-info"></span>
          <ng-content />
        </div>
      }
    }
  `
})
export class NotificationComponent {
  type = input<NotificationType>('info');
}

Блок @switch використовує строге порівняння (===), що усуває неочікувану поведінку, пов'язану з приведенням типів.

Вкладення блоків control flow

Блоки control flow можна вільно вкладати для створення складної логіки рендерингу:

html
@if (isLoading()) {
  <app-skeleton-loader />
} @else {
  @for (category of categories(); track category.id) {
    <section class="category">
      <h2>{{ category.name }}</h2>
      @for (product of category.products; track product.id) {
        @switch (product.availability) {
          @case ('in_stock') {
            <app-product-card [product]="product" />
          }
          @case ('pre_order') {
            <app-preorder-card [product]="product" />
          }
          @default {
            <app-out-of-stock-card [product]="product" />
          }
        }
      } @empty {
        <p>У цій категорії немає продуктів</p>
      }
    </section>
  } @empty {
    <p>Немає категорій для відображення</p>
  }
}

Оптимізації продуктивності з новим синтаксисом

Новий синтаксис control flow приносить значні покращення продуктивності:

Швидший Change Detection

Блоки @for з виразом track дозволяють Angular ефективніше порівнювати колекції. Фреймворк може пропускати перевірку елементів, які не змінилися, базуючись на ідентичності відстежуваного значення.

Краща мініфікація

Оскільки блоковий синтаксис не вимагає імпортів з CommonModule, невикористані блоки можуть бути повністю видалені під час production збірки.

Оптимізація @defer

Синтаксис control flow працює з @defer для ледачого завантаження:

html
@defer (on viewport) {
  @for (item of heavyItems(); track item.id) {
    <app-heavy-component [data]="item" />
  }
} @placeholder {
  <div class="placeholder">Завантаження...</div>
}

Міграція зі структурних директив

Процес міграції простий завдяки вбудованій схемі:

bash
ng generate @angular/core:control-flow

Схема автоматично перетворює:

Старий синтаксисНовий синтаксис
*ngIf="condition"@if (condition) { }
*ngIf="condition; else elseBlock"@if (condition) { } @else { }
*ngFor="let item of items"@for (item of items; track item) { }
*ngFor="let item of items; trackBy: trackFn"@for (item of items; track trackFn($index, item)) { }
[ngSwitch] + *ngSwitchCase@switch + @case

Готовий до співбесід з Angular?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Питання на співбесідах щодо Angular Control Flow

Нижче наведено набір питань, які часто задають на технічних співбесідах про новий синтаксис control flow.

Питання 1: Чому вираз track є обов'язковим у @for?

Відповідь: Вираз track є обов'язковим, оскільки надає алгоритму порівняння Angular унікальний ідентифікатор для кожного елемента в колекції. Це дозволяє фреймворку ефективно визначати, які елементи DOM слід повторно використати, оновити або видалити під час змін у колекції. Без відстеження Angular мусив би повторно рендерити весь список при кожній зміні, що суттєво впливало б на продуктивність.

Питання 2: Як @if обробляє глибоко вкладені опціональні властивості?

Відповідь: Блок @if підтримує аліасування змінних за допомогою ключового слова as, яке дозволяє витягти значення з вкладеного виразу та використовувати його в блоці. Синтаксис: @if (вираз; as аліас). Аліасована змінна доступна лише коли вираз істинний, що усуває потребу повторювати оператори опціонального ланцюжка в шаблоні.

Питання 3: Чим @switch відрізняється від *ngSwitch?

Відповідь: @switch вбудований у фреймворк і не вимагає імпортів. Він використовує нативний блоковий синтаксис замість директивних атрибутів. Усуває потребу в обгорткових елементах ng-container. Використовує строге порівняння (===), що запобігає неочікуваним співпадінням через приведення типів. Кожен @case та @default є незалежними блоками, що покращує читабельність коду.

Питання 4: Як отримати доступ до індексу та інших метаданих у @for?

Відповідь: Блок @for надає контекстні змінні через синтаксис let: @for (item of items; track item.id; let i = $index, f = $first). Доступні змінні: $index, $first, $last, $even, $odd та $count. Ці змінні можна присвоїти локальним аліасам і використовувати в блоці.

Питання 5: Чи можна використовувати @for з об'єктами Map і Set?

Відповідь: Так, блок @for ітерує по будь-якому об'єкту, що реалізує протокол ітератора JavaScript, включаючи Map, Set та генератори. Для Map ітерація повертає пари [ключ, значення], які можна деструктурувати в шаблоні.

Питання 6: Як працює @empty і коли він викликається?

Відповідь: Блок @empty відображається коли колекція в @for порожня (має нульову довжину) або коли ітерабельний об'єкт не повертає жодних елементів. Це вбудований еквівалент перевірки items.length === 0 та рендерингу альтернативного вмісту. Блок @empty є необов'язковим.

Питання 7: Які переваги продуктивності нового синтаксису control flow?

Відповідь: Новий синтаксис пропонує кілька переваг продуктивності: відсутність накладних витрат на імпорти (нуль додаткового коду для директив), кращий tree-shaking (невикористані шляхи коду усуваються), ефективніший change detection завдяки обов'язковому відстеженню, та можливість майбутніх оптимізацій компілятора, оскільки синтаксис парситься статично замість інтерпретації в runtime.

Найкращі практики для синтаксису Control Flow

При впровадженні нового синтаксису control flow варто дотримуватися кількох принципів:

  1. Завжди використовуйте унікальні ідентифікатори в track - надавайте перевагу track item.id замість track $index для колекцій, що можуть змінюватися
  2. Використовуйте @empty для кращого UX - завжди визначайте стан порожнього списку для кращого досвіду користувача
  3. Уникайте складної логіки у виразах - переносьте складні обчислення до компонента
  4. Застосовуйте аліасування для вкладених властивостей - використовуйте as для глибоко вкладених опціональних виразів
  5. Мігруйте поступово - обидва синтаксиси можуть співіснувати, тому можна мігрувати файл за файлом

Висновок

Синтаксис control flow в Angular представляє фундаментальну зміну в способі написання шаблонів. Блоки @if, @for та @switch пропонують інтуїтивніший синтаксис, натхненний JavaScript, усувають потребу імпорту CommonModule та забезпечують кращу продуктивність завдяки обов'язковому відстеженню та оптимізаціям компілятора. Для команд, що розпочинають нові проекти Angular, новий синтаксис має бути вибором за замовчуванням. Для існуючих проектів автоматична міграція за допомогою схеми ng generate @angular/core:control-flow забезпечує плавний перехід без ризику регресії.

Щоденний виклик

Чи знайдеш ти помилку в Angular?

Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Anthony Fillion-Maillet

Автор:

Anthony Fillion-Maillet

Засновник SharpSkill

Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.

Оновлено 3 вересня 2026 р.

Теги

#angular
#angular-17
#angular-control-flow
#frontend
#typescript

Поділитися

Пов'язані статті