Кастомные директивы в Angular позволяют добавлять собственное поведение к элементам DOM. Рассмотрим процесс создания как атрибутных, так и структурных директив.
Атрибутные директивы изменяют внешний вид или поведение элемента.
ng generate directive highlight
import { Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@Input() appHighlight = ''; // Цвет через входное свойство
@Input() defaultColor = 'yellow'; // Значение по умолчанию
constructor(
private el: ElementRef,
private renderer: Renderer2
) {}
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.appHighlight || this.defaultColor);
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight('');
}
private highlight(color: string) {
this.renderer.setStyle(
this.el.nativeElement,
'backgroundColor',
color
);
}
}
<p [appHighlight]="'lightblue'" defaultColor="pink">
Наведи на меня курсор
</p>
Структурные директивы изменяют структуру DOM.
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appUnless]'
})
export class UnlessDirective {
private hasView = false;
@Input() set appUnless(condition: boolean) {
if (!condition && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {}
}
<div *appUnless="isHidden">
Этот блок виден, если isHidden = false
</div>
import { Directive, Optional, Host } from '@angular/core';
import { SomeComponent } from './some.component';
@Directive({
selector: '[appChild]'
})
export class ChildDirective {
constructor(
@Optional() @Host() private parent: SomeComponent
) {
if (parent) {
parent.registerChild(this);
}
}
}
@Directive({
selector: '[appTooltip]'
})
export class TooltipDirective {
@Input() appTooltip = '';
private tooltipElement!: HTMLElement;
@HostListener('mouseenter') onMouseEnter() {
this.showTooltip();
}
private showTooltip() {
this.tooltipElement = document.createElement('div');
this.tooltipElement.textContent = this.appTooltip;
// Добавляем стили и позиционирование
document.body.appendChild(this.tooltipElement);
}
}
Пример теста для HighlightDirective:
describe('HighlightDirective', () => {
let fixture: ComponentFixture<TestComponent>;
let paragraph: DebugElement;
beforeEach(() => {
fixture = TestBed.configureTestingModule({
declarations: [HighlightDirective, TestComponent]
}).createComponent(TestComponent);
paragraph = fixture.debugElement.query(By.css('p'));
fixture.detectChanges();
});
it('should change background color on mouseenter', () => {
paragraph.triggerEventHandler('mouseenter', null);
expect(paragraph.nativeElement.style.backgroundColor).toBe('lightblue');
});
});
@Component({
template: `<p [appHighlight]="'lightblue'">Test</p>`
})
class TestComponent {}
создание кастомных директив в Angular включает генерацию класса с декоратором @Directive, реализацию необходимой логики и регистрацию в модуле. Атрибутные директивы модифицируют элементы, а структурные — изменяют DOM. Использование Renderer2, HostListener и правильное управление ресурсами — ключ к созданию эффективных директив.