Директивы атрибутов
Директивы атрибутов прикрепляют поведение к элементам.
Директива атрибута изменяет внешний вид или поведение элемента DOM.
Содержание
- Обзор директив
- Создание простой директивы атрибута
- Применение директивы атрибута к элементу в шаблоне
- Реагирование на события, инициированные пользователем
- Передача значений в директиву с привязкой данных @Input
- Привязка ко второму свойству
Попробуйте.
Обзор директив
В Angular существуют три типа директив:
- Компоненты — директивы с шаблоном.
- Структурные директивы — изменяют макет DOM, добавляя и удаляя элементы DOM.
- Директивы атрибутов — изменяют внешний вид или поведение элемента, компонента или другой директивы.
Компоненты являются наиболее распространёнными из этих трёх директив. Вы впервые столкнулись с компонентом в руководстве по быстрому началу.
Структурные директивы изменяют структуру представления. Два примера — NgFor и NgIf. Подробнее об этом в руководстве по структурным директивам.
Директивы атрибутов используются как атрибуты элементов. Например, встроенная директива NgStyle в руководстве по синтаксису шаблонов может одновременно изменить несколько стилей элемента.
Создание простой директивы атрибута
Директива атрибута минимально требует создания класса контроллера, аннотированного @Directive, который определяет селектор, идентифицирующий атрибут. Класс контроллера реализует желаемое поведение директивы.
На этой странице показано создание простой директивы атрибута myHighlight для установки цвета фона элемента при наведении курсора на этот элемент. Вы можете применить её следующим образом:
<p myHighlight>Highlight me!</p>
Напишите код директивы
Следуйте инструкциям по настройке для создания нового локального проекта с именем attribute-directives.
Создайте следующий исходный файл в указанной папке:
src/app/highlight.directive.ts
import { Directive, ElementRef, Input } from '@angular/core';
@Directive({ selector: '[myHighlight]' })
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
}
}
Утверждение import указывает символы из Angular core:
-
Directiveпредоставляет функциональность декоратора@Directive. -
ElementRefвводит в конструктор директивы, чтобы код мог получить доступ к элементу DOM. -
Inputпозволяет данным перетекать из выражения привязки в директиву.
Далее функция декоратора @Directive содержит метаданные директивы в объекте конфигурации в качестве аргумента.
@Directive требует селектора CSS для идентификации HTML в шаблоне, связанного с директивой. Селектор CSS для атрибута — это имя атрибута в квадратных скобках. Здесь селектор директивы — [myHighlight]. Angular ищет все элементы в шаблоне, у которых есть атрибут с именем myHighlight.
Почему не назвать её "highlight"?
Хотя highlight — более краткое имя, чем myHighlight, и оно будет работать, лучшей практикой является префикс имен селекторов, чтобы избежать конфликтов со стандартными атрибутами HTML. Это также снижает риск столкновения с именами директив сторонних производителей.
Убедитесь, что вы не добавляете префикс
ngк имени директивыhighlight, так как этот префикс зарезервирован для Angular, и его использование может привести к ошибкам, которые сложно диагностировать. Для демонстрации простой операции короткий префиксmyпомогает отличить вашу собственную директиву.
После метаданных @Directive следует класс контроллера директивы, называемый HighlightDirective, который содержит логику для директивы. Экспорт HighlightDirective делает его доступным для других компонентов.
Angular создаёт новый экземпляр класса контроллера директивы для каждого соответствующего элемента, вводя в конструктор Angular ElementRef. ElementRef — это служба, которая предоставляет прямой доступ к элементу DOM через свойство nativeElement.
Применение директивы атрибута
Чтобы использовать новую директиву HighlightDirective, создайте шаблон, который применяет директиву в качестве атрибута к абзацу (<p>) элементу. В терминах Angular элемент <p> является атрибутом хоста.
Поместите шаблон в свой собственный файл app.component.html, который выглядит так:
src/app/app.component.html
<h1>My First Attribute Directive</h1> <p myHighlight>Highlight me!</p>
Теперь сошлитесь на этот шаблон в AppComponent:
src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {
color: string;
}
Далее добавьте утверждение import для извлечения директивы Highlight и добавления этого класса в метаданные модуля declarations. Таким образом, Angular распознаёт директиву, когда встречает myHighlight в шаблоне.
src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HighlightDirective } from './highlight.directive';
@NgModule({
imports: [ BrowserModule ],
declarations: [
AppComponent,
HighlightDirective
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Теперь, когда приложение запускается, директива myHighlight выделяет текст абзаца.
Ваша директива не работает?
Вы добавили директиву к атрибуту
declarationsэлемента@NgModule? Это легко забыть! Откройте консоль в инструментах браузера и ищите ошибку такого типа:EXCEPTION: Template parse errors: Can't bind to 'myHighlight' since it isn't a known property of 'p'.Angular обнаружил, что вы пытаетесь привязаться к чему-то, но не может найти эту директиву в массиве
declarationsмодуля. После указанияHighlightDirectiveв массивеdeclarations, Angular знает, что может применить директиву к компонентам, объявленным в этом модуле.
Подводя итог, Angular нашёл атрибут myHighlight в элементе <p>. Он создал экземпляр класса HighlightDirective и ввёл ссылку на элемент <p> в конструктор директивы, который установил стиль фона элемента <p> в жёлтый цвет.
Реагирование на события, инициированные пользователем
В настоящее время myHighlight просто устанавливает цвет элемента. Директива может быть более динамичной. Она может определять, когда пользователь вводит или выходит из элемента, и реагировать установкой или очисткой цвета выделения.
Начните с добавления HostListener в список импортированных символов; добавьте также символ Input, так как вам это скоро понадобится.
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
Затем добавьте два обработчика событий, реагирующие на ввод и выход мыши, каждый из которых украшен декоратором HostListener.
@HostListener('mouseenter') onMouseEnter() {
this.highlight('yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
Декоратор @HostListener позволяет подписываться на события элемента DOM, в котором размещена директива атрибута, в данном случае <p>.
Конечно, вы могли бы обратиться к DOM с помощью стандартного JavaScript и добавить обработчики событий вручную. У такого подхода, по крайней мере, есть три проблемы:
- Вам нужно правильно написать обработчики.
- Код должен отсоединить обработчик, когда директива уничтожается, чтобы избежать утечки памяти.
- Прямое обращение к API DOM не является лучшей практикой.
Обработчики передают вызов вспомогательному методу, который устанавливает цвет на элемент DOM, el, который вы объявляете и инициализируете в конструкторе.
src/app/highlight.directive.ts (конструктор)
constructor(private el: ElementRef) { }
Вот обновлённая директива полностью:
src/app/highlight.directive.ts
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[myHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@HostListener('mouseenter') onMouseEnter() {
this.highlight('yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
Запустите приложение и убедитесь, что цвет фона появляется при наведении курсора на p и исчезает при его перемещении.
Передача значений в директиву с привязкой данных @Input
В настоящее время цвет выделения жёстко закодирован внутри директивы. Это негибко. В этом разделе вы даёте разработчику возможность устанавливать цвет выделения при применении директивы.
Начните с добавления свойства highlightColor в класс директивы следующим образом:
src/app/highlight.directive.ts (highlightColor)
@Input() highlightColor: string;
Привязка к свойству @Input
Обратите внимание на декоратор @Input. Он добавляет метаданные в класс, делающие свойство highlightColor директивы доступным для привязки.
Это свойство называется входным, поскольку данные перетекают из выражения привязки в директиву. Без этих метаданных Angular отклоняет привязку; см. ниже для получения дополнительной информации об этом.
Попробуйте добавить следующие варианты привязки директивы в шаблон AppComponent:
src/app/app.component.html (excerpt)
<p myHighlight highlightColor="yellow">Highlighted in yellow</p> <p myHighlight [highlightColor]="'orange'">Highlighted in orange</p>
Добавьте свойство color в AppComponent.
src/app/app.component.ts (класс)
export class AppComponent {
color = 'yellow';
}
Пусть он управляет цветом выделения с помощью привязки свойства.
src/app/app.component.html (excerpt)
<p myHighlight [highlightColor]="color">Highlighted with parent component's color</p>
Это хорошо, но было бы неплохо одновременно применить директиву и установить цвет в одном и том же атрибуте, как это:
<p [myHighlight]="color">Highlight me!</p>
Атрибут привязки [myHighlight] одновременно применяет директиву выделения к элементу <p> и устанавливает цвет выделения директивы с помощью привязки свойства. Вы повторно используете селектор атрибута директивы ([myHighlight]), чтобы выполнить обе задачи. Это чёткий и компактный синтаксис.
Вам нужно переименовать свойство highlightColor директивы в myHighlight, поскольку это теперь имя привязки свойства цвета.
src/app/highlight.directive.ts (переименовано для соответствия селектору директивы)
@Input() myHighlight: string;
Это неприемлемо. Слово, myHighlight, — ужасное имя свойства, и оно не передает намерения свойства.
Связывание со псевдонимом @Input
К счастью, вы можете назвать свойство директивы как угодно и присвоить ему псевдоним для целей связывания.
Восстановите исходное имя свойства и укажите селектор в качестве псевдонима в аргументе @Input.
src/app/highlight.directive.ts (свойство цвета с псевдонимом)
@Input('myHighlight') highlightColor: string;
Внутри директивы свойство известно как highlightColor. Снаружи директивы, где вы его связываете, оно известно как myHighlight.
Вы получаете лучшее из обоих миров: имя свойства, которое вам нужно, и синтаксис связывания, который вам нужен:
<p [myHighlight]="color">Highlight me!</p>
Теперь, когда вы связываете highlightColor, измените метод onMouseEnter() для его использования. Если кто-то пропустит связывание с highlightColor, выделение будет красным:
src/app/highlight.directive.ts (вход мыши)
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || 'red');
}
Вот последняя версия класса директивы.
src/app/highlight.directive.ts (фрагмент)
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[myHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@Input('myHighlight') highlightColor: string;
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || 'red');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
Создайте утилиту для проверки
Сложно представить, как работает эта директива. В этом разделе вы превратите AppComponent в утилиту, которая позволяет выбирать цвет выделения с помощью радиокнопки и связывает выбранный цвет с директивой.
Обновите app.component.html следующим образом:
<h1>My First Attribute Directive</h1> <h4>Pick a highlight color</h4> <div> <input type="radio" name="colors" (click)="color='lightgreen'">Green <input type="radio" name="colors" (click)="color='yellow'">Yellow <input type="radio" name="colors" (click)="color='cyan'">Cyan </div> <p [myHighlight]="color">Highlight me!</p>
Переработайте AppComponent.color так, чтобы у него не было начального значения.
export class AppComponent {
color: string;
}
Вот утилита и директива в действии.

Связывание со вторым свойством
Эта директива выделения имеет одно настраиваемое свойство. В реальном приложении может потребоваться больше.
В настоящее время цвет по умолчанию — цвет, который применяется до тех пор, пока пользователь не выберет цвет выделения — жёстко задан как «красный». Позвольте разработчику шаблона устанавливать цвет по умолчанию.
Добавьте второе свойство ввода HighlightDirective с именем defaultColor:
src/app/highlight.directive.ts (defaultColor)
@Input() defaultColor: string;
Измените onMouseEnter директивы так, чтобы она сначала пыталась выделить цвет с помощью highlightColor, затем с помощью defaultColor, и возвращалась к «красному», если оба свойства не определены.
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || this.defaultColor || 'red');
}
Как связаться со вторым свойством, когда вы уже связываете атрибут myHighlight?
Как и в компонентах, вы можете добавлять любые необходимые привязки к свойствам директивы, объединяя их в шаблоне. Разработчик должен иметь возможность написать следующий шаблон HTML, чтобы связать как AppComponent.color , так и использовать «фиолетовый» в качестве цвета по умолчанию.
<p [myHighlight]="color" defaultColor="violet"> Highlight me too! </p>
Angular понимает, что привязка defaultColor принадлежит директиве HighlightDirective, так как вы сделали ее общедоступной с помощью декоратора @Input.
Вот как утилита должна работать, когда вы закончите кодирование.

Резюме
На этой странице показано, как:
- Создать директиву атрибута, которая изменяет поведение элемента.
- Применить директиву к элементу в шаблоне.
- Реагировать на события, которые изменяют поведение директивы.
- Связать значения с директивой.
Следующий исходный код:
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {
color: string;
}
<h1>My First Attribute Directive</h1> <h4>Pick a highlight color</h4> <div> <input type="radio" name="colors" (click)="color='lightgreen'">Green <input type="radio" name="colors" (click)="color='yellow'">Yellow <input type="radio" name="colors" (click)="color='cyan'">Cyan </div> <p [myHighlight]="color">Highlight me!</p> <p [myHighlight]="color" defaultColor="violet"> Highlight me too! </p>
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[myHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@Input() defaultColor: string;
@Input('myHighlight') highlightColor: string;
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || this.defaultColor || 'red');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HighlightDirective } from './highlight.directive';
@NgModule({
imports: [ BrowserModule ],
declarations: [
AppComponent,
HighlightDirective
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Attribute Directives</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>loading...</my-app>
</body>
</html>
Вы также можете ознакомиться и загрузить .
Приложение: Зачем добавлять @Input?
В этой демонстрации свойство hightlightColor является входным свойством HighlightDirective. Вы видели его применение без псевдонима:
@Input() highlightColor: string;
Вы видели его с псевдонимом:
@Input('myHighlight') highlightColor: string;
В любом случае, декоратор @Input сообщает Angular, что это свойство общедоступно и доступно для связывания родительским компонентом. Без @Input Angular откажется от связывания со свойством.
Вы связывали HTML шаблона со свойствами компонента раньше и не использовали @Input. Что в этом разнице?
Разница заключается в доверии. Angular рассматривает шаблон компонента как принадлежащий компоненту. Компонент и его шаблон доверяют друг другу неявно. Поэтому шаблон самого компонента может связываться с любым свойством этого компонента, с псевдонимом или без него.
Но компонент или директива не должны слепо доверять другим компонентам и директивам. Свойства компонента или директивы по умолчанию скрыты от связывания. Они приватные с точки зрения связывания Angular. Когда они украшены декоратором @Input, свойство становится общедоступным с точки зрения связывания Angular. Только тогда им может воспользоваться другой компонент или директива.
Вы можете определить, нужно ли @Input , по расположению имени свойства в привязке.
-
Когда оно появляется в выражении шаблона справа от знака равенства (=), оно принадлежит компоненту шаблона и не требует декоратора
@Input. -
Когда оно появляется в квадратных скобках ([ ]) слева от знака равенства (=), свойство принадлежит другому компоненту или директиве; это свойство должно быть украшено декоратором
@Input.
Теперь примените это к следующему примеру:
<p [myHighlight]="color">Highlight me!</p>
-
Свойство
colorв выражении справа принадлежит компоненту шаблона. Шаблон и его компонент доверяют друг другу. Свойствуcolorне требуется декоратор@Input. -
Свойство
myHighlightслева относится к псевдониму свойстваHighlightDirective, а не к свойству компонента шаблона. Проблемы с доверием существуют. Следовательно, свойство директивы должно иметь декоратор@Input.
© 2010–2017 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://v2.angular.io/docs/ts/latest/guide/attribute-directives.html