Что такое директивы, созданные пользователем?
Пользовательские директивы в 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 и реализации структурных паттернов.