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

Директивы в Angular: как создавать и применять пользовательские директивы

6 min read Frontend Обновлено 26 Apr 2026
Директивы Angular: создание и использование
Директивы Angular: создание и использование

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

Angular предоставляет мощный набор встроенных директив и позволяет создавать собственные директивы, чтобы инкапсулировать поведение и повторно использовать его в шаблонах.

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

Директивы — это классы, помеченные декоратором @Directive, которые изменяют поведение или внешний вид HTML-элемента. Коротко:

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

Пример встроенных директив: ngIf (структурная) и ngFor (структурная), ngClass/ngStyle (атрибутные).

Ключевая идея: если нужно инкапсулировать небольшое поведение, используйте директивы; если нужен независимый визуальный блок с шаблоном — компонент.

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

  • Повторное использование логики без копирования шаблонов.
  • Разделение ответственности: шаблон отвечает за разметку, директива — за поведение.
  • Легкая тестируемость: директивы — обычные классы, их можно покрыть unit-тестами.
  • Производительность: структурные директивы управляют созданием/удалением представлений по необходимости.

Важно: плохое управление жизненным циклом или манипуляция DOM напрямую может привести к утечкам памяти и проблемам с SSR.

Начало работы: установка и создание проекта

Установите Angular CLI и создайте проект (код для терминала):

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

Команда создаст проект с именем custom-directives-app.

Создание атрибутной директивы: базовый highlight

Создайте файл src/app/highlight.directive.ts и определите класс с декоратором @Directive:

import { Directive } from "@angular/core";  
  
@Directive({  
  selector: "[myHighlight]",  
})  
export class HighlightDirective {  
  constructor() {}  
}  

Применить директиву в шаблоне можно так:

Some text

В этом виде директива ничего не делает — это «флаг» в шаблоне. Дальше добавим поведение.

Добавление поведения: ElementRef vs Renderer2

Самый простой способ изменить стиль — инжектировать ElementRef и напрямую менять nativeElement. Но это может привести к проблемам при серверном рендеринге (SSR) и потенциальным уязвимостям. Рекомендуется использовать Renderer2 для безопасной абстракции манипуляций DOM.

Пример с ElementRef (из учебного примера):

import { Directive, ElementRef } from "@angular/core";  
  
@Directive({  
    selector: "[myHighlight]"  
})  
export class HighlightDirective {  
    constructor(private element: ElementRef) {  
        this.element.nativeElement.style.backgroundColor = 'lightblue';  
    }  
}  

Рекомендованный пример с Renderer2:

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, 'background-color', 'lightblue');  
  }  
}  

Почему Renderer2: он абстрагирует низкоуровневые операции, работает с SSR и Web Workers и уменьшает риск XSS при корректном использовании.

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

Не забудьте зарегистрировать директиву в AppModule:

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 { }  

Теперь директиву можно применять в шаблонах:

Some text

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

ng serve  

Откройте http://localhost:4200/ чтобы увидеть результат.

Скриншот интерфейса приложения custom-directives-app

Передача значения в директиву через @Input

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

import { Directive, ElementRef, Input, Renderer2 } from "@angular/core";  
  
@Directive({ selector: '[myHighlight]' })  
export class HighlightDirective {  
  @Input() set myHighlight(color: string) {  
    const value = color || 'transparent';  
    this.renderer.setStyle(this.el.nativeElement, 'background-color', value);  
  }  
  
  constructor(private el: ElementRef, private renderer: Renderer2) {}  
}  

В шаблоне:

Some text

Можно также указать алиас для входного свойства, если хотите другой синтаксис:

@Input('myHighlight') set highlight(color: string) { ... }

Жизненный цикл директив и очистка

Директивы поддерживают хуки жизненного цикла (OnInit, OnChanges, OnDestroy и др.). Если вы подписываетесь на события или создаете таймеры, удаляйте подписки в ngOnDestroy(), чтобы избежать утечек.

Пример очистки:

import { OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

export class ExampleDirective implements OnDestroy {
  private sub = new Subscription();

  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}

Создание структурной директивы: condition

Структурные директивы работают с TemplateRef и ViewContainerRef. TemplateRef представляет шаблон, а ViewContainerRef — контейнер в DOM, куда вставляются представления.

Файл src/app/condition.directive.ts:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'
  
@Directive({  
    selector: "[condition]"  
})  
  
export class ConditionDirective {  
  
    @Input() set condition(arg: boolean) {  
        if(arg) {  
            this.viewContainer.createEmbeddedView(this.template)  
        } else {  
            this.viewContainer.clear();  
        }  
    }  
  
    constructor(  
            private template: TemplateRef,   
            private viewContainer: ViewContainerRef   
        ) {}  
}  

Применение в шаблоне (микросинтаксис):

Hello There!!!

Под капотом Angular превращает конструкцию *condition в и вызывает ваш directive-сеттер, передавая TemplateRef. Для реализации «else» можно принимать ещё и другую TemplateRef через дополнительное входное свойство или через API типа conditionElse.

Пример расширения для else:

@Input() set condition(arg: boolean) { ... }
@Input('conditionElse') elseTemplate?: TemplateRef;

Частые расширения и полезные паттерны

  • exportAs: позволяет дать директиве имя для доступа через template reference variable (#ref=”directiveName”).
  • HostBinding / HostListener: связывать классы, стили и реакции на события хоста.
  • Инъекция зависимостей: директивы могут инжектировать сервисы, другие директивы или элементы.
  • Использование ChangeDetectorRef для ручного управления обнаружением изменений при необходимости.

Пример HostListener и HostBinding:

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

@Directive({ selector: '[hoverClass]' })
export class HoverClassDirective {
  @HostBinding('class.hover') isHover = false;

  @HostListener('mouseenter') onEnter() { this.isHover = true; }
  @HostListener('mouseleave') onLeave() { this.isHover = false; }
}

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

  • Вам нужен сложный компонент с собственным шаблоном и стилями — используйте компонент.
  • Нужно выполнить вычисление чистой трансформации данных для отображения — лучше pipe (конвейер).
  • Когда поведение зависит от маршрутизации и жизненных циклов компонентов, бывает проще реализовать сервис и вызывать его из компонента.

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

  • Компоненты: для визуально значимого UI-блока с шаблоном.
  • Сервисы: для бизнес-логики и хранения состояния.
  • Pipes: для преобразования данных в шаблонах при отображении.

Выбор: если задача — повлиять на DOM-элемент (напр., повесить слушатель или поменять стиль) — директива. Если нужно отрисовать фрагмент шаблона — компонент.

Мини-методология: шаги разработки директивы

  1. Спроектировать API директивы (атрибут/структура, входы, выводы, алиасы).
  2. Начать с тестов: unit-тесты ожидаемого поведения в шаблоне.
  3. Реализовать класс с @Directive, инжектировать требуемые зависимости.
  4. Применять Renderer2 вместо прямых операций с nativeElement.
  5. Обработать жизненный цикл (OnInit, OnDestroy) и отписки.
  6. Добавить примеры использования и документацию в README компонента.

Чеклисты по ролям

Developer:

  • Определил API директивы и входные значения.
  • Использовал Renderer2 вместо nativeElement, где возможно.
  • Добавил unit-тесты и e2e-примеры использования.

Reviewer:

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

QA:

  • Проверил поведение директивы на разных браузерах и размерах экрана.
  • Протестировал edge-case (null/undefined/пустые строки в входах).

DevOps/CI:

  • Убедился, что bundle size не вырос критически (если директив много).
  • Проверил автотесты и линтеры.

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

  • Директива документирована и имеет примеры в репозитории.
  • Unit-тесты покрывают основные ветки (true/false, изменения входов).
  • Нет утечек (ngOnDestroy отписывает/очищает ресурсы).
  • Используется Renderer2 для DOM-операций, когда это важно.

Безопасность и производительность

  • Избегайте вставки HTML через innerHTML без санитайзинга.
  • Для стилей и классов используйте Renderer2.setStyle / addClass / removeClass.
  • Минимизируйте работу в главном потоке: не выполняйте тяжёлые вычисления в директиве; выносите их в сервисы или Web Worker.
  • Структурные директивы, которые часто создают/очищают представления, могут привести к перерисовкам — профилируйте при масштабировании.

Пример расширенной директивы: alias, exportAs и реакция на изменения

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

@Directive({ selector: '[myHighlight]', exportAs: 'myHighlight' })
export class HighlightDirective implements OnChanges {
  @Input('myHighlight') color?: string;

  constructor(private el: ElementRef, private renderer: Renderer2) {}

  ngOnChanges(changes: SimpleChanges) {
    const c = changes['color']?.currentValue || 'transparent';
    this.renderer.setStyle(this.el.nativeElement, 'background-color', c);
  }
}

Теперь в шаблоне можно получить доступ к директиве через reference:

Text

Принятие решений: когда выбирать директиву или компонент (дерево)

flowchart TD
  A[Нужна логика, завязанная на DOM-элементе?] -->|Да| B{Требуется собственный шаблон?}
  A -->|Нет| C[Используйте сервис или pipe]
  B -->|Да| D[Компонент]
  B -->|Нет| E[Директива]
  E --> F{Изменяет структуру DOM?}
  F -->|Да| G[Структурная директива]
  F -->|Нет| H[Атрибутная директива]

Примеры тест-кейсов и приёмочных критериев

  • Атрибутная директива должна корректно применять значение цвета при установке входа.
  • Изменение входного значения должно обновлять стиль без утечек.
  • Структурная директива должна показывать/скрывать содержимое при true/false.
  • Для else-ветки должно корректно рендериться альтернативное представление.

Совместимость и миграция

  • Большинство приёмов работы с директивами остаётся неизменными между версиями Angular, однако API низкоуровневых платформенных точек может эволюционировать. Проверяйте breaking changes в релиз-нотах при мажорных обновлениях.

Локальные примечания и уловки для российских проектов

  • Локализация текстов в шаблонах лучше держать в i18n-системе (Angular i18n или сторонние решения), но поведение директив обычно не завязано на локализации.
  • При работе с серверной частью (SSR) обязательно тестируйте директивы, которые обращаются к window/document.

Быстрые советы и шпаргалка

  • Если нужно просто изменить DOM-атрибут или класс — используйте HostBinding/HostListener.
  • Для безопасной модификации DOM — Renderer2.
  • Для условного рендеринга — структурная директива с TemplateRef и ViewContainerRef.
  • Всегда отписывайтесь от подписок в ngOnDestroy.

Заключение

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

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

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

Внезапный рост спама в почте: что делать
Кибербезопасность

Внезапный рост спама в почте: что делать

Wireshark: захват и анализ сетевых пакетов
Сетевой анализ

Wireshark: захват и анализ сетевых пакетов

Windows 8: устранение синих экранов и падений
Устранение проблем

Windows 8: устранение синих экранов и падений

Ссылка на часть веб‑страницы: Citebite и Highlighter
Инструменты

Ссылка на часть веб‑страницы: Citebite и Highlighter

Gmail как список для чтения — собрать и организовать
Продуктивность

Gmail как список для чтения — собрать и организовать

Как защититься от дипфейков — советы и план действий
Кибербезопасность

Как защититься от дипфейков — советы и план действий