ngFor в Angular — повторы, фильтрация, обратный порядок и вложенные объекты

Введение
Angular использует директивы для динамического формирования HTML на странице. Одна из основных структурных директив — ngFor. Она позволяет повторять один и тот же блок нужное число раз или итерировать по массиву объектов, чтобы показать их свойства. ngFor гибкая: поддерживает индексы, флаги first/last/odd/even и легко комбинируется с ngIf, ngClass и другими директивами.
Определение в одну строку: ngFor — структурная директива для повторного отображения шаблона для каждого элемента коллекции.
Важно: примеры в статье ориентированы на Angular 14+, но базовая синтаксическая модель ngFor стабильна в нескольких версиях Angular.
Содержание
- Как использовать ngFor для статического счёта
- Как пропускать или стилизовать отдельные элементы (odd/even)
- Как считать в обратном порядке
- Как стилизовать первый и последний элемент
- Как итерировать по объектам
- Вложенные ngFor (объекты внутри объектов)
- Практические советы, варианты отказа, чек-листы и готовые сниппеты
Как использовать ngFor для перебора статических чисел
Если нужно показать шаблон N раз, проще всего подготовить массив с нужной длиной и итерировать по нему. В Angular (v14) можно как создать статический список прямо в шаблоне, так и динамически сформировать массив в TypeScript.
Пример 1 — простой статический список в шаблоне (не рекомендуется для больших N):
Это повторяющийся параграф: {{ item }}
Пример 2 — динамическое создание массива в компоненте (лучше для произвольного размера):
export class ExampleClass implements OnInit {
numbers: Array = [];
constructor() {
// Создаёт массив [0,1,2,3,4,5,6,7,8,9]
this.numbers = Array(10).fill(1).map((x, i) => i);
}
} Итерирование в шаблоне:
Это повторяющийся параграф: {{ item }}
Примечание: если вам нужно рендерить очень большой диапазон чисел, рассмотрите виртуализацию списков (cdk-virtual-scroll-viewport) для оптимизации производительности.
Как пропускать или стилизовать отдельные элементы
ngFor предоставляет предопределённые локальные переменные: index, first, last, even, odd.
- odd — true для нечётных элементов (1, 3, 5… с точки зрения индексации 0-based это элементы с index%2===1)
- even — true для чётных элементов (index%2===0)
Пример CSS классов для стилизации:
.red {
color: red;
}
.blue {
color: blue;
}Применение классов в шаблоне через ngClass:
Это повторяющийся параграф: {{ item }}
Запустите приложение командой ng serve и откройте в браузере — вы увидите чередование стилей.
Если нужно полностью пропустить элементы по условию — комбинируйте ngFor и ngIf. Пример: выводить только чётные элементы:
Это повторяющийся параграф: {{ item }}
Совет по производительности: избегайте вложенного ngIf внутри ngFor для большого числа элементов; лучше фильтровать массив в компоненте (pipe или метод) и рендерить уже отфильтрованный массив.
Как считать в обратном порядке
Есть два простых подхода:
- Вычислять отображаемое значение по индексу (без создания нового массива).
- Создать перевёрнутую копию массива и итерировать по ней.
Пример — вывести индекс в обратном порядке, не меняя исходный массив:
Это повторяющийся параграф: {{ numbers.length - i - 1 }}
Пример с перевёрнутой копией в компоненте:
export class ExampleClass implements OnInit {
numbers: Array = [];
reversedList: Array = [];
constructor() {
this.numbers = Array(10).fill(1).map((x, i) => i);
this.reversedList = this.numbers.slice().reverse();
}
} Итерирование по reversedList:
Это повторяющийся параграф: {{ item }}
Когда использовать какой подход:
- Для простого отображения обратного индекса достаточно вычисления в шаблоне.
- Для сложной логики или если порядок влияет на структуру вложенных данных — лучше создать reversedList в компоненте.
Как стилизовать первый и последний элемент отдельно
ngFor даёт локальные переменные first и last. Это удобно, если нужно явно выделить первый и/или последний элемент, не полагаясь на CSS-псевдоклассы.
Пример:
Это повторяющийся параграф: {{ item }}
Преимущество: вы можете управлять классами или структурой DOM в зависимости от контекста элемента.
Как итерировать объекты
ngFor отлично работает с массивами объектов — достаточно дать имя переменной (например person) и обращаться к полям внутри шаблона.
Пример данных в компоненте:
export class ExampleClass implements OnInit {
people = [];
constructor() {
this.people = [
{ firstName: 'John', lastName: 'Smith', occupation: 'HR Manager', startDate: new Date('2019-02-05') },
{ firstName: 'Mary', lastName: 'Johnson', occupation: 'Technical Officer', startDate: new Date('2016-01-07') },
{ firstName: 'William', lastName: 'Brown', occupation: 'HR Officer', startDate: new Date('2018-03-03') },
];
}
}Шаблон для рендеринга:
{{ person.firstName }} {{ person.lastName }}
{{ person.occupation }}
{{ person.startDate | date: 'dd.MM.yyyy' }}
Здесь добавлен Angular DatePipe для локального формата даты (dd.MM.yyyy) — полезно для корректного отображения дат в русскоязычном интерфейсе.
Вложенные ngFor для объектов внутри объектов
Если объект содержит массив (например контакты экстренной связи), внутри внешнего ngFor можно объявить вложенный ngFor.
Пример данных с экстренными контактами:
this.people = [
{
firstName: 'John',
lastName: 'Smith',
emergencyContacts: [
{ name: 'Amanda Smith', relationship: 'Wife', phone: '0441239876' },
{ name: 'Barry Smith', relationship: 'Son', phone: '0442239876' }
]
},
{
firstName: 'Mary',
lastName: 'Johnson',
emergencyContacts: [
{ name: 'Mark Johnson', relationship: 'Husband', phone: '0443239876' }
]
},
];Вложенный шаблон для отображения контактов:
{{ person.firstName }} {{ person.lastName }}
Экстренные контакты:
{{ contact.name }}
{{ contact.relationship }}
{{ contact.phone }}
Обратите внимание на корректное использование полей contact и person — в примерах выше были опечатки (использование person.relationship вместо contact.relationship). В вложенных циклах всегда обращайтесь к текущей переменной вложенного цикла.
Практические советы и рекомендации
- Фильтрация и сортировка лучше выполняются в компоненте (или в отдельном pipe), а не “в шаблоне на лету” — так легче тестировать и оптимизировать.
- Для больших списков используйте виртуализацию (CDK virtual scroll) и trackBy, чтобы минимизировать переработку DOM.
- trackBy: добавьте функцию trackBy к ngFor, если элементы могут переупорядочиваться или обновляться — это сильно снижает число перестроений DOM.
Пример trackBy:
trackById(index: number, item: any) {
return item.id; // или другой уникальный ключ
}
- Избегайте выполнения тяжёлых вычислений прямо в шаблоне (например вызовов методов), так как Angular может вызывать их часто при детектировании изменений.
Когда ngFor не подойдёт — альтернативы
- Если требуется отрисовать огромные наборы данных — используйте серверную постраничную загрузку + виртуализацию.
- Для сложных трансформаций данных используйте RxJS + async pipe: подпишитесь на поток данных в компоненте и отдавайте в шаблон Observable через async pipe.
- Если нужно уникальное поведение для элемента (состояние, подписки), рассмотрите вынесение элемента в отдельный компонент с OnPush стратегией обнаружения изменений.
Частые ошибки и как их избежать
- Ошибка: использование одного и того же DOM-элемента для обновлений без trackBy → перерисовка всех элементов. Решение: trackBy.
- Ошибка: вызов методов в шаблоне для вычисления значения → частые вызовы. Решение: вычислить в компоненте или использовать мемоизацию.
- Ошибка: опечатка в поле при вложенном ngFor (person.phone вместо contact.phone). Решение: проверяйте имена переменных и пишите простые тесты.
Мини-методология: выбор подхода
- Оцените объём данных (малый / средний / большой).
- Если большой — используйте пагинацию / виртуализацию + trackBy.
- Если нужна локальная фильтрация — делайте её в компоненте или pipe.
- Для сложных элементов — выносите в отдельный компонент с OnPush.
Быстрая шпаргалка (cheat sheet)
- Синтаксис: *ngFor=”let item of items; let i = index; let odd = odd; let first = first”
- Переменные: index, first, last, odd, even
- Оптимизация: trackBy
- Стилизация: [ngClass] или [class.someClass] напрямую
- Управление рендером: комбинируйте ngIf и ngFor, либо фильтруйте массив в компоненте
Роли и чек-листы
Для разработчика:
- Использовать trackBy при динамическом списке
- Не вызывать тяжёлые методы в шаблоне
- Разбить сложные элементы на подкомпоненты
Для тестировщика:
- Проверить отображение при пустом массиве
- Проверить стиль первого/последнего элемента
- Проверить поведение при добавлении/удалении элементов
Для продакт-овнера:
- Утвердить требования по производительности для списков
- Решить, нужен ли серверный поиск/пагинация
Ментальные модели
- ngFor как “перебор коллекции и генератор DOM-шаблонов” — думайте о нём как о map в функциональном программировании, только для представления.
- trackBy — это идентификатор сущности в DOM: если он постоянен, виртуализация и диффинг работают быстрее.
Decision flowchart (Mermaid)
flowchart TD
A[Нужно вывести список?] --> B{Объём данных}
B -- Малый --> C[Использовать ngFor]
B -- Средний --> C
B -- Большой --> D[Рассмотреть виртуализацию или пагинацию]
C --> E{Нужно часто менять порядок/фильтр?}
E -- Да --> F[Фильтрация/сортировка в компоненте + trackBy]
E -- Нет --> G[Простая реализация в шаблоне]
D --> FКритерии приёмки
- Список корректно рендерится для пустого, малого и большого наборов данных.
- Первые и последние элементы получают нужные классы.
- Чётные/нечётные строки стилизуются согласно дизайну.
- Производительность не деградирует при изменении порядка: trackBy применяется, где нужно.
Краткое резюме
ngFor — простой и гибкий инструмент для повторного рендеринга компонентов и HTML-блоков. Для производительности используйте trackBy и виртуализацию при больших объёмах данных. Для логики фильтрации и сортировки лучше обрабатывать данные в компоненте, а не в шаблоне.
Важно: кроме ngFor, Angular предоставляет другие директивы для динамики: ngIf, ngSwitch, ngStyle, ngClass и ngModel — их сочетание даёт гибкую модель построения интерфейса.