Перейти к содержанию

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

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

Angular директивы атрибуты

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

zoom.directive.ts (version 1)

1
2
3
4
@Directive({
    selector: '[zoom]',
})
export class ZoomDirective {}

Квадратные скобки являются указателем того, что это именно директива атрибут.

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

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

zoom.directive.ts (version 2)

1
2
3
4
5
6
7
8
@Directive({
    selector: '[zoom]',
})
export class ZoomDirective {
    constructor(private el: ElementRef) {
        el.nativeElement.style.fontSize = '20px';
    }
}

Angular директива из примера предназначена в основном для тега <p>, исходя из условия, что размер шрифта по умолчанию равен 14px, она увеличивает это значение до 20px.

1
2
<p>This text is normal.</p>
<p zoom>This text is larger.</p>

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

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

zoom.directive.ts (version 3)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@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)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@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`;
    }
}

Пример использования.

1
<p zoom [zoomSize]="20">Hover text to make it larger.</p>

Здесь размер шрифта, который будет установлен при наведении курсора мыши, задается через входное свойство zoomSize.

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

1
2
3
4
5
//
export class ZoomDirective {
    @Input('zoom') size;
    //
}
1
<p [zoom]="20">Hover text to make it larger.</p>

Структурные Angular директивы

Основное отличие структурных директив — они видоизменяют DOM-структуру страницы.

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

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

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

1
<p *ngIf="true">Some text</p>

транслируется в

1
2
3
<ng-template [ngIf]="true">
    <p>Some text</p>
</ng-template>

Создадим Angular директиву *duplicateContent для создания копии элемента в зависимости от истинности переданного выражения.

duplicate-content.directive.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@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
    ) {}
}

Пример использования.

1
<div *duplicateContent="true">Content for duplication</div>

Создается структурная директива с применением декоратора @Directive(). Он принимает объект, в свойстве selector которого указывается наименование директивы в квадратных скобках.

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

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

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

Комментарии