Создание директив


Довольно часто при разработке Angular приложения приходится создавать пользовательские директивы (Angular custom directive).

Angular директивы атрибуты
Создание самой простой директивы атрибута ограничивается классом, обернутым декоратором @Directive() с заданием необходимой конфигурации.

zoom.directive.ts (version 1)


@Directive({
selector: '[zoom]',
})
export class ZoomDirective {}
Квадратные скобки являются указателем того, что это именно директива атрибут.

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

Для манипуляции элементом используется класс ElementRef из модуля @angular/core. Его свойство nativeElement предоставляет доступ к элементу по ссылке.

zoom.directive.ts (version 2)


@Directive({
selector: '[zoom]',
})
export class ZoomDirective {
constructor(private el: ElementRef) {
el.nativeElement.style.fontSize = '20px'
}
}
Angular директива из примера предназначена в основном для тега <p>, исходя из условия, что размер шрифта по умолчанию равен 14px, она увеличивает это значение до 20px.


<p>This text is normal.</p>
<p zoom>This text is larger.</p>
Сейчас zoom меняет только стилизацию элемента. Для того чтобы изменить поведение по умолчанию, используется декоратор @HostListener(). Теперь сделаем так, чтобы размер шрифта увеличивался только при наведении на элемент DOM-дерева курсора мыши. Иначе говоря, изменим стандартное поведение при возникновении пользовательского события.

@HostListener() также входит в состав @angular/core.

zoom.directive.ts (version 3)


@Directive({
selector: '[zoom]',
})
export class ZoomDirective {
constructor(private el: ElementRef) {}

@HostListener('mouseenter') onMouseEnter() {
this.setFontSize(20)
}

@HostListener('mouseleave') onMouseLeave() {
this.setFontSize(14)
}

setFontSize(size: number | string): void {
this.el.nativeElement.style.fontSize = `${size}px`
}
}
@HostListener() привязывает обработчики к событиям, возникающим по отношению к элементу с Angular директивой.

Но что, если поведение элемента, задаваемое ему директивой, зависит от значения внешнего фактора? Рассмотрим передачу внешних значений.

Делается это с использованием входных свойств.

zoom.directive.ts (version 4)


@Directive({
selector: '[zoom]',
})
export class ZoomDirective {
@Input('zoomSize') size

constructor(private el: ElementRef) {}

@HostListener('mouseenter') onMouseIn() {
this.setFontSize(this.size)
}

@HostListener('mouseleave') onMouseOut() {
this.setFontSize(14)
}

setFontSize(value: number | string): void {
this.el.nativeElement.style.fontSize = `${value}px`
}
}
Пример использования.


<p zoom [zoomSize]="20">Hover text to make it larger.</p>
Здесь размер шрифта, который будет установлен при наведении курсора мыши, задается через входное свойство zoomSize.

Чтобы не вводить лишнее свойство, можно сделать саму директиву zoom входным параметром.


//
export class ZoomDirective {
@Input('zoom') size
//
}

<p [zoom]="20">Hover text to make it larger.</p>
Структурные Angular директивы
Основное отличие структурных директив - они видоизменяют DOM-структуру страницы.

Отличительная их особенность - наличие перед ними символа *.

Префикс * лишь облегчает применение структурных директив, транслируя атрибут в <ng-template></ng-template>, служащий оберткой для элемента, к которому изначально была применена директива.

Например, запись


<p *ngIf="true">Some text</p>
транслируется в


<ng-template [ngIf]="true">
<p>Some text</p>
</ng-template>
Создадим Angular директиву *duplicateContent для создания копии элемента в зависимости от истинности переданного выражения.

duplicate-content.directive.ts


@Directive({
selector: '[duplicateContent]',
})
export class DuplicateContentDirective {
@Input() set duplicateContent(condition: boolean) {
if (condition && !this.contentWasDuplicated) {
this.vc.insert(this.tpl)
this.contentWasDuplicated = true
}
}

private contentWasDuplicated: boolean = false

constructor(
private tpl: TemplateRef<any>,
private vc: ViewContainerRef
) {}
}
Пример использования.


<div *duplicateContent="true">Content for duplication</div>
Создается структурная директива с применением декоратора @Directive(). Он принимает объект, в свойстве selector которого указывается наименование директивы в квадратных скобках.

Представление элемента, включая его самого, хранится в переменной templateRef, являющейся экземпляром класса TemplateRef. Контейнер представлений (элемент <ng-template/>) представлен переменной viewContainer. Подробно о представлениях.

Имея доступ к этим свойствам можно легко манипулировать DOM-элементом.

Обратите внимание, что приватное свойство contentWasDuplicated ограничивает создание более, чем одной копии содержимого.