1. Что такое структурные директивы?
Структурные директивы — это директивы, которые изменяют структуру DOM: добавляют или удаляют элементы в зависимости от условий. В Angular все структурные директивы начинаются с * (звёздочка), чтобы вы сразу могли их отличить от остальных.
Самые популярные структурные директивы в Angular:
- *ngIf — условное отображение элементов
- *ngFor — перебор коллекций (будет в следующей лекции)
- *ngSwitch — переключение между вариантами (продвинутый случай)
В этой лекции нас интересует именно *ngIf.
Зачем нужен *ngIf?
В реальных приложениях вы часто сталкиваетесь с ситуацией: "Показывать или не показывать этот блок?". Например:
- Кнопка "Войти" должна исчезнуть после входа пользователя.
- Сообщение об ошибке появляется только если что-то пошло не так.
- Блок загрузки показывается только во время загрузки данных.
Для всего этого в Angular есть *ngIf. Он работает как шлагбаум: если условие истинно — элемент появляется в DOM, если ложно — исчезает. Причём элемент реально удаляется из DOM, а не просто "прячется" стилями.
2. Как работает *ngIf? Примеры
Базовый синтаксис
<div *ngIf="условие">
Этот блок появится, если условие истинно!
</div>
- Если условие — true, блок появится на странице.
- Если false — элемента не будет даже в DOM!
Пример 1: Простейшее использование
// app.component.ts
export class AppComponent {
isLoggedIn = false;
}
<!-- app.component.html -->
<button *ngIf="!isLoggedIn">Войти</button>
<button *ngIf="isLoggedIn">Выйти</button>
Если пользователь не вошёл — будет кнопка "Войти". Вошёл — появится "Выйти". Всё просто!
Пример 2: Сообщение об ошибке
errorMessage: string | null = null;
<div *ngIf="errorMessage" class="error">
{{ errorMessage }}
</div>
Если errorMessage не пустая строка и не null — блок появится и покажет текст ошибки.
3. Как работает *ngIf внутри?
Когда Angular видит *ngIf, он делает магию: превращает ваш элемент в специальный шаблон <ng-template>, который будет вставлен в DOM только если условие истинно. Если условие ложно — шаблон даже не попадает в DOM.
Визуализация
<div *ngIf="isVisible">Привет!</div>
Angular превращает это примерно в такое:
<ng-template [ngIf]="isVisible">
<div>Привет!</div>
</ng-template>
То есть элемент не просто скрывается — его реально нет на странице, пока условие не станет истинным. Это экономит ресурсы и предотвращает случайные баги, связанные с "невидимыми" элементами.
4. Альтернатива: else в *ngIf
Иногда нужно показать что-то другое, если условие не выполнено. Для этого Angular поддерживает конструкцию else. Пример:
<div *ngIf="isLoading; else loadedBlock">
Загрузка...
</div>
<ng-template #loadedBlock>
<div>Данные загружены!</div>
</ng-template>
- Если isLoading — true, будет "Загрузка...".
- Если false — появится содержимое из шаблона #loadedBlock.
Более компактный пример
<div *ngIf="user; else guestBlock">
Добро пожаловать, {{ user.name }}!
</div>
<ng-template #guestBlock>
<div>Пожалуйста, войдите в систему.</div>
</ng-template>
5. Сравнение с React: "if" в JSX
В React нет встроенного аналога *ngIf, но условный рендеринг делается через обычный JavaScript.
Пример 1: Тернарный оператор
{isLoggedIn ? <button>Выйти</button> : <button>Войти</button>}
Пример 2: &&-оператор
{errorMessage && <div className="error">{errorMessage}</div>}
Пример 3: if-else перед return
let content;
if (isLoading) {
content = <div>Загрузка...</div>;
} else {
content = <div>Данные загружены!</div>;
}
return <>{content}</>;
Главное отличие:
- В React вы пишете логику прямо на JavaScript внутри JSX.
- В Angular — используете декларативную конструкцию в шаблоне (*ngIf).
Таблица сравнения
| Angular (*ngIf) | React (JSX) |
|---|---|
|
|
|
|
| Нет элемента в DOM, если false | Нет элемента в DOM, если false |
| Условия — только выражения шаблона | Можно любые JS-выражения |
6. Вложенные *ngIf и блоки else if
В Angular нет прямого аналога else if для *ngIf. Но можно легко организовать вложенные условия через несколько блоков:
<div *ngIf="status === 'loading'">Загрузка...</div>
<div *ngIf="status === 'error'">Ошибка!</div>
<div *ngIf="status === 'success'">Успех!</div>
Или с использованием else и дополнительных шаблонов:
<div *ngIf="status === 'loading'; else notLoading">Загрузка...</div>
<ng-template #notLoading>
<div *ngIf="status === 'error'; else successBlock">Ошибка!</div>
</ng-template>
<ng-template #successBlock>
<div>Успех!</div>
</ng-template>
Честно говоря, если хочется много вложенных условий — проще вынести логику в компонент и сделать одну переменную, которую проверять.
7. Практика: добавляем условный рендеринг в ваше приложение
Допустим, мы делаем простое TODO-приложение и хотим показывать сообщение "Нет задач", если список пуст, и список задач — если не пуст.
Шаг 1. В компоненте
export class TodoComponent {
todos: string[] = [];
}
Шаг 2. В шаблоне
<div *ngIf="todos.length === 0">
Нет задач!
</div>
<ul *ngIf="todos.length > 0">
<li *ngFor="let todo of todos">{{ todo }}</li>
</ul>
После добавления задачи
addTodo(task: string) {
this.todos.push(task);
}
Результат:
- Пока задач нет — видим "Нет задач!"
- Как только добавили — появляется список.
8. Типичные ошибки при использовании *ngIf
Ошибка №1: Использование *ngIf вместе с hidden или display: none.
Если вы хотите полностью убрать элемент из DOM, используйте только *ngIf. Если добавить hidden или CSS-скрытие, элемент останется в DOM, а это не всегда безопасно (например, в формах).
Ошибка №2: Ожидание, что переменные сразу обновят DOM.
Иногда переменная меняется, но Angular не обновляет DOM мгновенно (например, если забыли про асинхронные операции). Если используете асинхронные данные — убедитесь, что они реально обновились.
Ошибка №3: Использование сложных выражений в *ngIf.
Лучше выносить сложные вычисления в переменные компонента, а не писать монстрообразные выражения в шаблоне. Так проще поддерживать код и легче отлаживать.
Ошибка №4: Дублирование кода в блоках *ngIf и else.
Если большая часть разметки совпадает — лучше сделать одну разметку и менять только отличающиеся части, чтобы не плодить копипасту.
Ошибка №5: Ожидание, что *ngIf работает как в React.
В React условный рендеринг — это просто про "вернуть или не вернуть компонент". В Angular — про "есть элемент в DOM или его нет". Иногда это влияет и на жизненный цикл дочерних компонентов (например, при уничтожении компонента через *ngIf его состояние сбрасывается).
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ