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

В Angular директивы — это специальные классы с декоратором @Directive, которые позволяют расширять или изменять поведение элементов DOM в шаблоне. Они применяются к элементам через селекторы и могут управлять их внешним видом, поведением или структурой. Angular предоставляет встроенные директивы, а также позволяет создавать собственные.

1. Типы директив в Angular

Angular делит директивы на три основных типа:

1.1. Компоненты (Component)

  • Технически являются директивами с шаблоном.

  • Декоратор: @Component, основан на @Directive.

  • Имеют селектор и связанный шаблон.

Пример:

@Component({
selector: 'app-user-card',
template: \`<div>User card works!</div>\`
})
export class UserCardComponent {}

Все компоненты — это директивы с template, но не все директивы — компоненты.

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

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

  • Синтаксис начинается со звёздочки * (синтаксический сахар).

  • Примеры: *ngIf, *ngFor, *ngSwitch.

*ngIf

Показывает элемент при выполнении условия.

<p \*ngIf="isLoggedIn">Добро пожаловать</p>

Реализация:

@Directive({ selector: '\[ngIf\]' })
export class NgIf { ... }

*ngFor

Повторяет элемент по коллекции.

<li \*ngFor="let user of users">{{ user.name }}</li>

*ngSwitch, *ngSwitchCase, *ngSwitchDefault

Работают как switch-case в шаблоне:

<div \[ngSwitch\]="role">
<p \*ngSwitchCase="'admin'">Админ</p>
<p \*ngSwitchCase="'user'">Пользователь</p>
<p \*ngSwitchDefault>Гость</p>
</div>

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

  • Изменяют поведение или внешний вид существующего элемента.

  • Не изменяют структуру DOM.

  • Примеры: ngClass, ngStyle, кастомные директивы.

ngClass

Применяет CSS-классы:

<div \[ngClass\]="{ active: isActive, hidden: isHidden }"></div>

ngStyle

Применяет inline-стили:

<div \[ngStyle\]="{ color: isError ? 'red' : 'black' }"></div>

Кастомная директива:

@Directive({
selector: '\[appHighlight\]'
})
export class HighlightDirective {
constructor(private el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
}
}
<p appHighlight>Этот текст будет подсвечен</p>

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

Пример атрибутной директивы:

@Directive({
selector: '\[appRedText\]'
})
export class RedTextDirective {
constructor(private el: ElementRef) {
el.nativeElement.style.color = 'red';
}
}
<p appRedText>Красный текст</p>

Использование Renderer2 для безопасности:

constructor(private el: ElementRef, private renderer: Renderer2) {
this.renderer.setStyle(this.el.nativeElement, 'color', 'blue');
}

Добавление @Input:

@Input() appHighlight = 'yellow';
ngOnInit() {
this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', this.appHighlight);
}
<p \[appHighlight\]="'green'">Зелёный фон</p>

3. Особенности *-синтаксиса (syntactic sugar)

Директива *ngIf="isVisible" преобразуется Angular в следующую форму:

<ng-template \[ngIf\]="isVisible">
<p>Текст</p>
</ng-template>

То есть * — это сокращение, которое говорит Angular об использовании ng-template с директивой.

4. Инъекции зависимостей в директивах

В директивы можно внедрять:

  • ElementRef — прямой доступ к DOM-элементу.

  • Renderer2 — безопасная работа с DOM (для SSR).

  • HostListener — реакция на события.

  • HostBinding — привязка к свойствам host-элемента.

Пример с HostListener:

@Directive({
selector: '\[appHoverHighlight\]'
})
export class HoverHighlightDirective {
@HostListener('mouseenter') onMouseEnter() {
this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', 'yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.renderer.removeStyle(this.el.nativeElement, 'backgroundColor');
}
constructor(private el: ElementRef, private renderer: Renderer2) {}
}

5. Пример структурной директивы с ViewContainerRef и TemplateRef

@Directive({
selector: '\[appUnless\]'
})
export class UnlessDirective {
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {}
@Input() set appUnless(condition: boolean) {
if (!condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}
<p \*appUnless="isVisible">Показывается, когда isVisible == false</p>

6. Отличие компонентов от других директив

Характеристика Компонент Директива
Декоратор @Component @Directive
--- --- ---
Шаблон Да (template) Нет
--- --- ---
UI-элемент Да Нет
--- --- ---
Использование <app-...> [app-...] или *app-...
--- --- ---

7. Объявление директивы в модуле

Любую директиву необходимо объявить в declarations соответствующего модуля:

@NgModule({
declarations: \[
AppComponent,
HighlightDirective
\]
})
export class AppModule {}

Для повторного использования можно экспортировать директиву через SharedModule.

8. Часто используемые встроенные директивы

Директива Тип Назначение
*ngIf Структурная Условный рендеринг элементов
--- --- ---
*ngFor Структурная Итерация по коллекции
--- --- ---
*ngSwitch Структурная Альтернатива if-else
--- --- ---
ngClass Атрибутная Управление CSS-классами
--- --- ---
ngStyle Атрибутная Управление inline-стилями
--- --- ---
ngModel Атрибутная Двусторонняя привязка формы (требует FormsModule)
--- --- ---