Директивы Angular

f

Почему большинство разработчиков используют директивы неправильно?

Вы когда-нибудь писали *ngIf внутри *ngFor и замечали, что страница начинает тормозить? Это не баг, а следствие непонимания того, как Angular перерисовывает DOM. На типичных курсах вам покажут синтаксис — как написать ngClass или создать свою директиву с декоратором @Directive. Но вам не расскажут, что одна неправильно размещённая структурная директива может увеличить время рендеринга на 300%. Вы будете смотреть на панель производительности и гадать, почему приложение 'висит'.

Дело в том, что Angular — это не просто шаблонизатор. Каждая директива — это микро-компонент, работающий внутри цикла обнаружения изменений. И если вы не понимаете, как именно Angular отслеживает изменения, то рискуете создать 'узкое горлышко'. В этом руководстве вы узнаете о трёх разных подходах к работе с директивами: от стандартных встроенных до создания собственных с тонкими настройками. Вы увидите, чем они отличаются не только синтаксически, но и по производительности, читаемости и гибкости.

Главная ловушка, в которую попадают новички: думать, что *ngIf и ngSwitch взаимозаменяемы. Они — нет. Первый уничтожает и создаёт элемент заново, второй — просто переключает отображение. Это критически важно для форм с ngModel: если вы используете *ngIf, состояние формы сбросится. Вы потеряете данные, и будете искать причину не в том месте. Давайте разберёмся, как этого избежать.

Подход 1: Стандартные встроенные директивы — ваш ежедневный инструмент

Когда вы берёте NgIf, NgFor, NgClass или NgStyle, вы пользуетесь самыми оптимизированными решениями. Однако, как показывает практика, 70% разработчиков используют их неправильно. Например, NgFor делает рендеринг каждого элемента через примитивную проверку ссылок. Если вы передаёте массив, который мутируется (добавляете или удаляете элементы без замены ссылки), Angular не узнает об изменениях.

Вы будете в замешательстве: данные в сервисе обновились, но список на экране — нет. Решение — использовать иммутабельность или трекинг-функцию trackBy. Без неё Angular при каждом изменении массива уничтожает и создаёт все элементы заново. Это дикая нагрузка на процессор, особенно если в списке 500+ карточек с изображениями. Профессионалы всегда пишут trackBy — это первое, что проверяют на техсобеседованиях.

Ещё один нюанс: NgIf поддерживает контекстную переменную else и then. Мало кто знает, что внутри else можно передать не только ссылку на ng-template, но и контекстные данные. Это позволяет не дублировать код, а переиспользовать один и тот же шаблон с разными условиями. Вы сэкономите 100+ строк кода, если начнёте применять эту технику.

Подход 2: Кастомные атрибутные директивы — как нарисовать себе суперсилу

Атрибутные директивы — это то, что меняет поведение или внешний вид существующего элемента. Вы пишете один класс с декоратором @Directive, и используете его как обычный атрибут. Это мощный инструмент, который даёт вам контроль над DOM без создания лишних компонентов. Представьте: нужно подсветить все элементы с ошибками валидации. Вместо того чтобы добавлять класс вручную в 40 местах, вы пишете одну директиву [appInputError], которая автоматически анализирует FormControl и меняет цвет рамки.

Вы почувствуете разницу в скорости разработки сразу. Одна директива может слушать события (клик, наведение, ввод), взаимодействовать с 3-4 другими директивами через @Host, и даже внедрять сервисы. Но есть подводный камень: кастомные директивы не должны изменять структуру шаблона — только стили или поведение. Если вам нужно добавить/удалить элемент, это уже структурная директива. Смешивание концепций приведёт к багам, которые сложно отловить.

Совет от практиков: используйте Renderer2 вместо прямого доступа к свойству style элемента. Renderer2 безопасен для серверного рендеринга и зон (zone.js). Вы будете удивлены, сколько ошибок в production возникают из-за того, что кто-то написал element.style.display = 'none' вместо renderer.setStyle(element, 'display', 'none'). Это особенно критично в 2026 году, когда серверный рендеринг стал стандартом.

Подход 3: Структурные кастомные директивы — манипуляция DOM с умом

Структурные директивы — это те, которые используют звёздочку * (например, *myPermission). Они меняют структуру DOM: добавляют, удаляют или заменяют элементы. В отличие от атрибутных, они создают встроенный view (embeddedViewRef). Это даёт фантастическую гибкость: вы можете полностью контролировать, когда и как рендерить содержимое. Например, создайте директиву *appHasRole, которая проверяет права доступа. Если у пользователя нет роли — элемент даже не попадёт в DOM, а не просто скроется через display: none.

Вы будете поражены, как легко с помощью структурных директив можно реализовать сложную логику: рендеринг placeholder'ов, отложенную загрузку контента (lazy loading), или даже виртуальный скроллинг под капотом. Но будьте осторожны: каждая структурная директива создаёт новый lifecycle-контекст. Если вложить три структурные директивы, у вас получится три уровня вложенных проверок изменений. Это может снизить производительность.

Профессиональный трюк: используйте микросинтаксис с шаблонными переменными. Например, в *ngFor вы можете получить index, first, last. В своей кастомной директиве вы тоже можете создавать контекстные переменные. Это позволяет передавать данные из директивы в шаблон, делая код лаконичным. В 2026 году Angular поддерживает мощный API для шаблонных контекстов — используйте TemplateRef и ViewContainerRef на полную.

  1. Плюс: Максимальный контроль. Вы решаете, когда вставлять элемент, с каким контекстом и когда удалять.
  2. Минус: Сложность отладки. Ошибки в структурных директивах часто не бросаются в глаза, а проявляются как неперерендеренный контент.
  3. Совет: Всегда создавайте unit-тесты для структурных директив. Встроенный комплаер Angular генерит сложные фабрики, и ручные проверки тут обязательны.

Какой подход выбрать новичку, а какой — профессионалу?

Если вы только начинаете свой путь, придерживайтесь правила 80/20: 80% задач решайте встроенными директивами (NgIf, NgFor, NgClass), а 20% — кастомными атрибутными, только когда стандартные не справляются. Структурные кастомные директивы — это уровень senior и team lead. Не пытайтесь создать свою *ngFor с первого дня — это приведёт к разочарованию.

Для профессионалов, которые читают этот текст, совет обратный: перестаньте использовать встроенные директивы для сложной бизнес-логики. Напишите кастомную структурную директиву для проверки прав, другую — для логирования кликов, третью — для аналитики. Это сделает код чище, а компоненты — тоньше. Вы увидите, как компоненты превратятся из “500 строк” в “50 строк”, а вся логика переедет в переиспользуемые сущности.

И самое важное: помните про контекст. Когда вы пишете директиву, думайте о том, что её будут использовать другие разработчики (или вы сами через месяц). Делайте API понятным, используйте TypeScript строго. Если ваша директива принимает 10 параметров — это плохой дизайн. Разбейте на несколько маленьких. В конце концов, именно умение декомпозировать директивы отличает инженера от копипастера.

Итоговая рекомендация: что реально стоит внедрить завтра

Не тратьте время на написание велосипедов. Ваша задача — выбрать подход под конкретную задачу. Для управления состоянием отображения (показать/скрыть) — используйте *ngIf с else и контекстными переменными. Для манипуляций стилями — NgClass или кастомная атрибутная директива, если логика повторяется. Для сложных манипуляций с домом (условный рендеринг в зависимости от прав доступа, типа контента) — пишите структурную директиву.

Главный совет для 2026 года: не забывайте про сигналы. Хотя директивы работают в зонах, Angular сигналы могут сделать ваши кастомные директивы ещё более производительными. Экспериментируйте с effect() внутри директив. Но помните: сигналы не отменяют жизненный цикл. Подписываться в ngOnInit, отписываться в ngOnDestroy — это правило остаётся священным.

В итоге: изучите встроенные директивы до мелочей (особенно trackBy и контекстные переменные), затем освойте атрибутные (с Renderer2 и @HostListener), и только потом беритесь за структурные. Постепенно вы сможете писать такие директивы, которые будут работать быстрее, чем стандартные решения, потому что будете точно знать, когда и что обновлять. Это и есть профессионализм.

Добавлено: 23.04.2026