Шаблоны Angular поддерживают блоки control flow, которые позволяют условно показывать, скрывать и повторять элементы.
Условный показ контента с @if, @else if и @else
Блок @if условно показывает своё содержимое, когда выражение условия truthy:
@if (a > b) {
<p>{{ a }} is greater than {{ b }}</p>
}
Если нужно показать альтернативный контент, можно предоставить любое число блоков @else if и один блок @else.
@if (a > b) {
{{ a }} is greater than {{ b }}
} @else if (b > a) {
{{ a }} is less than {{ b }}
} @else {
{{ a }} is equal to {{ b }}
}
Ссылка на результат условного выражения
Условный @if поддерживает сохранение результата условного выражения в переменную для переиспользования внутри блока.
@if (user.profile.settings.startDate; as startDate) {
{{ startDate }}
}
Это полезно для ссылок на более длинные выражения, которые проще читать и поддерживать внутри шаблона.
Повтор контента блоком @for
Блок @for проходит по коллекции и многократно рендерит содержимое блока. Коллекция может быть любым JavaScript iterable, но Angular имеет дополнительные оптимизации производительности для значений Array.
Типичный цикл @for выглядит так:
@for (item of items; track item.id) {
{{ item.name }}
}
Блок @for Angular не поддерживает statements, изменяющие поток, вроде JavaScript continue или break.
Почему track в блоках @for важен?
Выражение track позволяет Angular поддерживать связь между вашими данными и DOM-узлами на странице. Это позволяет Angular оптимизировать производительность, выполняя минимум необходимых DOM-операций при изменении данных.
Эффективное использование track может значительно улучшить производительность рендеринга приложения при обходе коллекций данных.
В выражении track выберите свойство, которое уникально идентифицирует каждый элемент. Если модель данных включает уникально идентифицирующее свойство — обычно id или uuid — используйте это значение. Если в данных нет такого поля, настоятельно рассмотрите его добавление.
Для статических коллекций, которые никогда не меняются, можно использовать $index, чтобы Angular отслеживал каждый элемент по его индексу в коллекции.
Если других вариантов нет, можно использовать сам элемент как tracking key. Это говорит Angular отслеживать элемент по reference identity через оператор тройного равенства (===). Избегайте этого варианта по возможности: он может привести к значительно более медленным обновлениям рендера, так как Angular не сможет сопоставить, какой элемент данных соответствует каким DOM-узлам.
@for (item of items; track item) {
{{ item.name }}
}
ПРИМЕЧАНИЕ: В отличие от *ngFor, блок @for приоритизирует переиспользование view. Если отслеживаемое свойство изменилось, но ссылка на объект осталась той же, Angular обновляет привязки view (включая inputs компонента), а не уничтожает и заново создаёт весь элемент.
Контекстные переменные в блоках @for
Внутри блоков @for всегда доступны несколько неявных переменных:
| Переменная | Значение |
|---|---|
$count |
Число элементов в обходимой коллекции |
$index |
Индекс текущей строки |
$first |
Является ли текущая строка первой |
$last |
Является ли текущая строка последней |
$even |
Является ли индекс текущей строки чётным |
$odd |
Является ли индекс текущей строки нечётным |
Эти переменные всегда доступны с этими именами, но их можно алиасить через сегмент let:
@for (item of items; track item.id; let idx = $index, e = $even) {
<p>Item #{{ idx }}: {{ item.name }}</p>
}
Aliasing полезен при вложенных блоках @for, позволяя читать переменные внешнего блока @for из внутреннего блока @for.
Fallback для блоков @for через блок @empty
Опционально можно включить секцию @empty сразу после содержимого блока @for. Содержимое блока @empty показывается, когда элементов нет:
@for (item of items; track item.name) {
<li>{{ item.name }}</li>
} @empty {
<li>There are no items.</li>
}
Условный показ контента блоком @switch
Хотя блок @if подходит для большинства сценариев, блок @switch предоставляет альтернативный синтаксис для условного рендера данных. Его синтаксис близко напоминает statement switch в JavaScript.
@switch (userPermissions) {
@case ('admin') {
<app-admin-dashboard />
}
@case ('reviewer')
@case ('editor') {
<app-editor-dashboard />
}
@default {
<app-viewer-dashboard />
}
}
Значение условного выражения сравнивается с выражением case через оператор тройного равенства (===).
У @switch нет fallthrough, поэтому эквивалент statement break или return в блоке не нужен.
Можно указать несколько условий для одного блока, используя подряд идущие statements @case.
Опционально можно включить блок @default. Содержимое блока @default показывается, если ни одно из предшествующих выражений case не совпало со значением switch.
Если ни один @case не совпал с выражением и блока @default нет, ничего не показывается.
Исчерпывающая проверка типов
@switch поддерживает исчерпывающую проверку типов, позволяя Angular во время компиляции проверить, что все возможные значения union type обработаны.
Используя @default never;, вы явно объявляете, что оставшихся case быть не должно. Если union type позже расширят и новый case не покрыт @case, template type checker Angular сообщит об ошибке, помогая рано поймать пропущенные ветки.
@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';
}
Когда переключаемое выражение вложено в union, нужно явно указать выражение для проверки исчерпываемости.
@Component({
template: `
@switch (state.mode) {
@case ('show') {
{{ state.menu }};
}
@case ('hide') {}
@default never(state);
}
`,
})
export class App {
state!: {mode: 'hide'} | {mode: 'show'; menu: number};
}