Что такое директивы, созданные пользователем?

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

1. Типы пользовательских директив

Атрибутивные директивы (Attribute Directives)

Изменяют внешний вид или поведение существующего DOM-элемента (стили, классы, обработчики и т.п.).

Примеры:

  • Директива, выделяющая текст.

  • Директива, добавляющая класс при наведении.

Структурные директивы (Structural Directives)

Изменяют структуру DOM — добавляют, удаляют или перемещают элементы.

Примеры:

  • Кастомный аналог *ngIf.

  • Директива повторения элементов с условиями фильтрации.

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

import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';
@Directive({
selector: '\[appHighlight\]'
})
export class HighlightDirective {
constructor(private el: ElementRef, private renderer: Renderer2) {}
@HostListener('mouseenter')
onMouseEnter() {
this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'yellow');
}
@HostListener('mouseleave')
onMouseLeave() {
this.renderer.removeStyle(this.el.nativeElement, 'backgroundColor');
}
}

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

<p appHighlight>Наведи на меня</p>

Эта директива подсвечивает элемент при наведении.

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

import {
Directive,
Input,
TemplateRef,
ViewContainerRef
} from '@angular/core';
@Directive({
selector: '\[appIfTrue\]'
})
export class IfTrueDirective {
constructor(
private templateRef: TemplateRef<any>,
private vcr: ViewContainerRef
) {}
@Input() set appIfTrue(condition: boolean) {
this.vcr.clear();
if (condition) {
this.vcr.createEmbeddedView(this.templateRef);
}
}
}

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

<div \*appIfTrue="showBlock">Этот блок виден, если showBlock === true</div>

Поведение аналогично *ngIf.

4. Реакция на изменения входных параметров

Атрибутивные директивы могут принимать параметры через @Input():

@Input('appStyle') highlightColor: string;

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

<p \[appStyle\]="'red'">Красный текст</p>

5. Использование HostListener и HostBinding

  • @HostListener позволяет слушать события хоста (элемента, к которому применяется директива).

  • @HostBinding позволяет привязать свойства директивы к DOM-свойствам хоста.

Пример:

@HostBinding('style.color') color: string;
@HostListener('mouseenter') onEnter() {
this.color = 'blue';
}

6. Инъекция зависимостей в директиву

Директива может инжектировать:

  • ElementRef — доступ к элементу.

  • Renderer2 — безопасное изменение DOM.

  • NgControl, NgModel — доступ к формам.

  • Любые пользовательские сервисы.

7. Повторное использование и изоляция

Пользовательские директивы можно включать в отдельные модули (например, SharedModule), чтобы переиспользовать в разных частях приложения.

@NgModule({
declarations: \[HighlightDirective\],
exports: \[HighlightDirective\]
})
export class SharedModule {}

8. Примеры применения пользовательских директив

Валидатор поля

@Directive({
selector: '\[appEmailValidator\]',
providers: \[{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => EmailValidatorDirective),
multi: true
}\]
})
export class EmailValidatorDirective implements Validator {
validate(control: AbstractControl): ValidationErrors | null {
const emailRegex = /\\S+@\\S+\\.\\S+/;
const valid = emailRegex.test(control.value);
return valid ? null : { invalidEmail: true };
}
}

Автоматический фокус

@Directive({
selector: '\[appAutoFocus\]'
})
export class AutoFocusDirective {
constructor(private el: ElementRef) {}
ngAfterViewInit() {
this.el.nativeElement.focus();
}
}

Подсказка (Tooltip)

@Directive({
selector: '\[appTooltip\]'
})
export class TooltipDirective {
@Input() appTooltip = '';
private tooltipEl?: HTMLElement;
constructor(private el: ElementRef) {}
@HostListener('mouseenter')
show() {
this.tooltipEl = document.createElement('span');
this.tooltipEl.textContent = this.appTooltip;
this.tooltipEl.className = 'tooltip';
document.body.appendChild(this.tooltipEl);
const rect = this.el.nativeElement.getBoundingClientRect();
this.tooltipEl.style.top = \`${rect.bottom + 5}px\`;
this.tooltipEl.style.left = \`${rect.left}px\`;
}
@HostListener('mouseleave')
hide() {
this.tooltipEl?.remove();
}
}

9. Angular Lifecycle в директивах

Директивы могут реализовывать жизненные хуки:

  • ngOnInit()

  • ngOnChanges()

  • ngOnDestroy()

  • ngAfterViewInit()

Это позволяет управлять ресурсами, отписками, логикой инициализации и очистки.

Пользовательские директивы — мощный инструмент Angular, позволяющий реализовывать повторно используемое поведение, не зависящее от структуры компонентов, и расширять HTML декларативным способом. Они идеально подходят для создания UI-интерактивности, валидации, манипуляции DOM и реализации структурных паттернов.