Как использовать ngIf и ngFor?

ngIf и ngFor — это структурные директивы в Angular, которые управляют отображением DOM-элементов на основе условий (ngIf) или итерации по массиву (ngFor). Они предшествуют элементу DOM и начинаются с символа *, что означает «структурную» директиву — такую, которая может изменять структуру DOM (например, добавлять или удалять элементы).

1. *ngIf: Условный рендеринг

1.1. Базовое использование

<div \*ngIf="isVisible">Этот блок будет показан, если isVisible === true</div>

Если значение переменной isVisible в компоненте истинно (true), то элемент отобразится. Если false — не будет отображён вовсе (Angular удаляет элемент из DOM).

1.2. Альтернатива с else

Можно использовать ngIf с else для показа альтернативного шаблона:

<div \*ngIf="isLoggedIn; else guestBlock">Добро пожаловать, пользователь!</div>
<ng-template #guestBlock>Пожалуйста, войдите в систему</ng-template>

Здесь ng-template — специальный контейнер, который Angular не рендерит до тех пор, пока его не вызовет ngIf.

1.3. С then и else

Более гибкий синтаксис:

<ng-container \*ngIf="userLoaded; then userBlock; else loadingBlock"></ng-container>
<ng-template #userBlock>Данные пользователя загружены</ng-template>
<ng-template #loadingBlock>Загрузка данных...</ng-template>

1.4. Использование as для создания локальной переменной

<div \*ngIf="user as currentUser">
Имя: {{ currentUser.name }}
</div>

2. *ngFor: Повторение элементов

ngFor позволяет создавать шаблонный элемент для каждого объекта в массиве.

2.1. Базовое использование

<ul>
<li \*ngFor="let item of items">{{ item }}</li>
</ul>

Если items = ['a', 'b', 'c'], то Angular отобразит три элемента <li> с соответствующими значениями.

2.2. Использование индекса

&lt;li \*ngFor="let item of items; let i = index"&gt;
{{ i + 1 }}. {{ item }}
&lt;/li&gt;

Здесь переменная i содержит текущий индекс итерации, начиная с 0.

2.3. Использование дополнительных переменных

  • index — индекс текущего элемента

  • first — true, если это первый элемент

  • last — true, если это последний элемент

  • even — true, если индекс чётный

  • odd — true, если индекс нечётный

&lt;div \*ngFor="let item of items; let isOdd = odd"&gt;
&lt;span \[ngClass\]="{ 'odd-row': isOdd }"&gt;{{ item }}&lt;/span&gt;
&lt;/div&gt;

3. Вложенные ngFor

&lt;div \*ngFor="let group of groups"&gt;
&lt;h3&gt;{{ group.name }}&lt;/h3&gt;
&lt;ul&gt;
&lt;li \*ngFor="let user of group.users"&gt;{{ user }}&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

В компоненте:

groups = \[
{ name: 'Админы', users: \['Анна', 'Олег'\] },
{ name: 'Пользователи', users: \['Иван', 'Мария'\] }
\];

4. Совмещение ngIf и ngFor

4.1. Через вложенные теги

&lt;ul \*ngIf="items.length &gt; 0">
&lt;li \*ngFor="let item of items"&gt;{{ item }}&lt;/li&gt;
&lt;/ul&gt;

4.2. Через ng-container

ng-container используется для логических конструкций без добавления лишнего DOM:

&lt;ng-container \*ngIf="items.length &gt; 0">
&lt;div \*ngFor="let item of items"&gt;{{ item }}&lt;/div&gt;
&lt;/ng-container&gt;

5. Работа с объектами

Angular не может напрямую итерироваться по объекту, только по массиву. Но можно преобразовать объект в массив:

objectEntries = Object.entries(myObject); // \[\['ключ1', 'значение1'\], ...\]
&lt;div \*ngFor="let \[key, value\] of objectEntries"&gt;
{{ key }}: {{ value }}
&lt;/div&gt;

6. Пример: Таблица с ngFor

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;#&lt;/th&gt;&lt;th&gt;Имя&lt;/th&gt;&lt;th&gt;Email&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr \*ngFor="let user of users; let i = index"&gt;
&lt;td&gt;{{ i + 1 }}&lt;/td&gt;
&lt;td&gt;{{ user.name }}&lt;/td&gt;
&lt;td&gt;{{ user.email }}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

7. Пример: ngIf + ngFor + trackBy

Когда Angular рендерит список через ngFor, он по умолчанию сравнивает объекты по ссылке, что может привести к перерендерингу всех элементов. Для оптимизации используется trackBy:

&lt;li \*ngFor="let item of items; trackBy: trackById"&gt;{{ item.name }}&lt;/li&gt;
trackById(index: number, item: any): any {
return item.id;
}

Это особенно полезно при работе с большими списками и динамически обновляемыми данными.

8. Использование ng-template с ngFor

Иногда нужно вынести шаблон итерации отдельно:

&lt;ng-container \*ngFor="let item of items; template: itemTpl"&gt;&lt;/ng-container&gt;
&lt;ng-template #itemTpl let-item&gt;
&lt;div&gt;{{ item.name }}&lt;/div&gt;
&lt;/ng-template&gt;

Альтернативно:

&lt;ng-template ngFor let-item \[ngForOf\]="items"&gt;
&lt;div&gt;{{ item }}&lt;/div&gt;
&lt;/ng-template&gt;

9. Пример использования ngIf с загрузкой

&lt;div \*ngIf="loading"&gt;Загрузка...&lt;/div&gt;
&lt;div \*ngIf="!loading && data"&gt;{{ data }}&lt;/div&gt;
&lt;div \*ngIf="!loading && !data"&gt;Нет данных&lt;/div&gt;

10. Антипаттерн: Совмещение *ngIf и *ngFor на одном элементе

Такой код:

&lt;li \*ngIf="condition" \*ngFor="let item of items"&gt;{{ item }}&lt;/li&gt;

не будет работать, так как Angular не позволяет использовать две структурные директивы на одном элементе. Правильный способ — обернуть в <ng-container>:

&lt;ng-container \*ngIf="condition"&gt;
&lt;li \*ngFor="let item of items"&gt;{{ item }}&lt;/li&gt;
&lt;/ng-container&gt;

11. Управление DOM при помощи ngIf и ngFor

  • *ngIf физически удаляет и вставляет элемент в DOM.

  • *ngFor создает копии шаблона для каждого элемента массива.

  • В обоих случаях Angular отслеживает изменения с помощью ChangeDetectorRef, и любое изменение входных данных вызывает пересчёт DOM.

ngIf и ngFor являются основными инструментами Angular-шаблонов и обеспечивают гибкий контроль за отображением элементов в DOM. Их грамотное использование позволяет выстраивать эффективные, адаптивные и производительные пользовательские интерфейсы.