Создание директив
Довольно часто при разработке 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 ограничивает создание более, чем одной копии содержимого.