Гид по технологиям

Angular: создание кастомных директив

6 min read Frontend Обновлено 30 Dec 2025
Angular: создание кастомных директив
Angular: создание кастомных директив

Логотип Angular: белая буква A на красном щитовидном фоне

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

Что такое директивы?

Директивы — это фрагменты кода в Angular, которые расширяют поведение HTML-элементов или компонентов. Простая формулировка: директива «подключается» к элементу и может изменить его разметку, стиль, слушать события или управлять созданием/удалением частей DOM.

Кратко о типах:

  • Атрибутные директивы (attribute directives) — меняют внешний вид или поведение элемента. Пример: подсветка, изменение стилей.
  • Структурные директивы (structural directives) — добавляют, удаляют или перемещают элементы в DOM с помощью представлений (views). Примеры: ngIf, ngFor.

Определение в одну строку: директива — класс с декоратором @Directive, который сообщает Angular, как и где применять этот класс.

Преимущества использования директив

  • Переиспользуемость: одна директива — много компонентов.
  • Разделение ответственности: логика поведения вынесена из компонентов в отдельные сущности.
  • Тестируемость: директивы легко покрывать юнит-тестами отдельно от UI.

Важно: директивы не должны становиться контейнерами бизнес-логики. Их задача — взаимодействие с DOM и представлением.

Настройка проекта Angular

Убедитесь, что у вас установлен Angular CLI и создан проект. Примеры команд (используйте терминал):

npm install -g @angular/cli
ng new custom-directives-app

После создания проекта откройте его в редакторе и переходите к src/app.

Создание атрибутной директивы (простейший пример)

Создайте файл src/app/highlight.directive.ts и определите директиву:

import { Directive } from '@angular/core';

@Directive({
  selector: '[myHighlight]',
})
export class HighlightDirective {
  constructor() {}
}

Пояснение: selector: ‘[myHighlight]’ значит — директиву можно применять как атрибут:

.

Пример использования в шаблоне:

Some text

Добавление поведения: доступ к элементу через ElementRef

Чтобы изменить DOM-элемент, используйте ElementRef. Пример: установка фонового цвета.

import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[myHighlight]'
})
export class HighlightDirective {
  constructor(private element: ElementRef) {
    this.element.nativeElement.style.backgroundColor = 'lightblue';
  }
}

Объяснение: Angular внедрит (DI) ElementRef, который содержит nativeElement — реальный DOM-узел. Менять стиль напрямую просто, но осторожно: это обходит механизм Angular верификации безопасности.

Важно: прямой доступ к nativeElement может привести к уязвимостям (XSS) и усложнить серверный рендеринг. По возможности используйте Renderer2.

Более правильный способ: Renderer2

Renderer2 — абстракция для безопасных манипуляций с DOM, совместимая с платформами, где нет браузерного DOM (SSR).

import { Directive, ElementRef, Renderer2 } from '@angular/core';

@Directive({
  selector: '[myHighlight]'
})
export class HighlightDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'lightblue');
  }
}

Используйте Renderer2, если проект предполагает универсальный рендеринг или вам важна безопасность манипуляций с DOM.

Подключение директивы в модуле

Не забудьте объявить директиву в app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HighlightDirective } from './highlight.directive';

@NgModule({
  declarations: [
    AppComponent,
    HighlightDirective,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

После этого директива доступна во всех шаблонах компонентов, объявленных в модуле.

Передача значения в директиву с помощью @Input

Чтобы директива принимала параметр (например, цвет), используйте @Input и setter:

import { Directive, ElementRef, Input } from '@angular/core';

@Directive({
  selector: '[myHighlight]'
})
export class HighlightDirective {
  @Input() set myHighlight(color: string) {
    this.element.nativeElement.style.backgroundColor = color;
  }

  constructor(private element: ElementRef) { }
}

Пример использования:

Some text

Подсказка: для реактивного обновления используйте setter с приведением типов и проверкой входных данных.

Создание структурной директивы (пример, аналог ngIf)

Структурные директивы работают с TemplateRef и ViewContainerRef: они создают или удаляют представления.

Создайте src/app/condition.directive.ts с кодом:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[condition]'
})
export class ConditionDirective {
  @Input() set condition(value: boolean) {
    if (value) {
      this.viewContainer.createEmbeddedView(this.template);
    } else {
      this.viewContainer.clear();
    }
  }

  constructor(
    private template: TemplateRef,
    private viewContainer: ViewContainerRef
  ) {}
}

Использование:

Hello There!!!

Пояснение: синтаксис *condition преобразуется компилятором в вызов с помощью и связывает TemplateRef с ViewContainerRef.

Когда использовать структурную директиву, а когда — атрибутную

  • Нужно добавить/удалить элемент целиком — структурная директива.
  • Нужно изменить стиль/поведение существующего элемента — атрибутная директива.

Ментальная модель: структурная директива манипулирует деревом компонентов (контейнеры и представления), атрибутная — объектом-элементом (стили, слушатели).

Критерии приёмки

  • Директива объявлена в модуле и импортируется без ошибок.
  • Применение директивы в шаблоне изменяет DOM согласно ожиданию.
  • Для директив с @Input значения корректно принимаются и применяются при изменении.
  • Нет прямых ошибок безопасности при использовании nativeElement; если используется nativeElement, это одобрено командой безопасности.
  • Unit-тесты покрывают ключевые ветви: создание, обновление и удаление представлений (для структурных).

Тестирование директив

Unit-тесты для директивы проверяют:

  • Инициализацию: директива создаётся и не выбрасывает исключений.
  • Поведение setter @Input: изменение входного значения приводит к ожидаемому эффекту.
  • Для структурных директив: при true — содержимое отображается, при false — скрывается.

Пример теста (псевдокод):

  • Создать TestComponent с шаблоном, использующим директиву.
  • Вставить в TestBed, получить fixture и проверить innerHTML/стили.

Отладка и лучшие практики

  • Используйте Renderer2 вместо прямых обращений к nativeElement, чтобы быть совместимым с SSR.
  • Минимизируйте побочные эффекты в конструкторе директивы; предпочтительнее делать инициализацию в ngOnInit.
  • Валидация входных данных: проверяйте типы и значения перед применением стилей.
  • Не храните бизнес-логику в директивах; держите их ответственными только за представление.
  • Для сложных операций используйте hostListener и hostBinding или экспортируйте API директивы для взаимодействия с компонентом.

Важно: при манипуляции DOM учитывайте производительность — частые прямые изменения стиля могут привести к переработке компоновки (reflow).

Альтернативные подходы

  • Компонент-обёртка вместо директивы — если нужно контролировать шаблон и структуру дочерних элементов.
  • Pipes — если задача связана с преобразованием данных в шаблоне, а не с DOM.
  • Сервисы/State Management — если поведение глобальное и должно быть разделяемым между различными компонентами.

Когда директивы не подходят (контрпримеры)

  • Нужна сложная логика рендеринга с собственным шаблоном — лучше компонент.
  • Требуется доступ к жизненному циклу дочерних компонентов на уровне шаблона — предпочтите компоненты с ng-content.
  • Логика тесно связана с бизнес-правилами и должна быть тестируема без DOM — выносите в сервисы.

Рольные чек-листы

Разработчик:

  • Объявить директиву в модуле.
  • Использовать Renderer2 для стилей.
  • Покрыть unit-тестами ключевое поведение.

Тестировщик:

  • Проверить визуальные изменения в разных сценариях.
  • Убедиться в отсутствии регрессий и утечек памяти (при создании/удалении представлений).

Владелец продукта:

  • Подтвердить пользовательские сценарии применения директивы.
  • Убедиться, что UX не ломается при отключении/включении директивы.

Шпаргалка: полезные API и шаблоны

  • @Directive({ selector: ‘[name]’ }) — объявление директивы.
  • ElementRef, Renderer2 — доступ и безопасные манипуляции с DOM.
  • @Input() — передача параметров в директиву.
  • TemplateRef, ViewContainerRef — создание структурных представлений.
  • hostBinding, hostListener — привязки к свойствам и событиям host-элемента.

Примеры быстрых сниппетов:

// host listener
import { HostListener } from '@angular/core';

@HostListener('mouseenter') onEnter() { /* ... */ }

// host binding
import { HostBinding } from '@angular/core';

@HostBinding('class.active') isActive = false;

Технические ограничения и совместимость

  • На серверной части (Angular Universal) избегайте прямых обращений к window/document без проверки (typeof window !== ‘undefined’).
  • Для сторонних платформ (web workers) используйте Renderer2 и абстракции.
  • Совместимость с Ivy: современные директивы работают с Ivy/NG View Engine, но проверьте target и compilation options в tsconfig для старых проектов.

Короткое руководство: шаги реализации (минимальная методология)

  1. Выбрать тип директивы — атрибутная или структурная.
  2. Сгенерировать файл и объявить класс с @Directive.
  3. Реализовать поведение через ElementRef/Renderer2 или TemplateRef/ViewContainerRef.
  4. Добавить @Input/@Output при необходимости API.
  5. Объявить директиву в модуле.
  6. Написать unit-тесты и протестировать в браузере (ng serve).

Краткое глоссарий

  • ElementRef — обёртка над нативным DOM-элементом.
  • Renderer2 — API для безопасных операций с DOM.
  • TemplateRef — шаблон, который можно рендерить как представление.
  • ViewContainerRef — контейнер для вставки представлений.

Пример запуска и проверки

Запустите dev-сервер:

ng serve

Откройте http://localhost:4200 и проверьте элементы, к которым применена директива.

Скриншот приложения custom-directives-app с примером подсветки

Резюме

Кастомные директивы — мощный инструмент Angular для расширения поведения DOM и создания переиспользуемых UI-паттернов. Используйте Renderer2 для безопасности и совместимости, покрывайте директивы тестами, и выбирайте между компонентом и директивой, исходя из того, нужно ли вам управлять шаблоном или только поведением элемента.

Важно: экспортируйте только тот минимальный API директивы, который действительно нужен, и избегайте смешивания бизнес-логики с манипуляциями DOM.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как выбрать детскую книгу — советы и ресурсы
Детские книги

Как выбрать детскую книгу — советы и ресурсы

HTML‑списки: ol, ul и dl — когда применять
HTML

HTML‑списки: ol, ul и dl — когда применять

Играть в игры Xbox 360 на Xbox One
Игры

Играть в игры Xbox 360 на Xbox One

Лучшие отели и цены с помощью Bing
Путешествия

Лучшие отели и цены с помощью Bing

Исправить DNS_PROBE_FINISHED_NXDOMAIN
Техника

Исправить DNS_PROBE_FINISHED_NXDOMAIN

Бесплатный сервер Minecraft: Minehut и Aternos
Игры

Бесплатный сервер Minecraft: Minehut и Aternos