Эта статья посвящена угловым директивам — что это такое, как их использовать и как создавать свои собственные.
Директивы, возможно, являются наиболее важной частью приложения Angular, и, если подумать, наиболее часто используемый модуль Angular, компонент, на самом деле является директивой.
Угловой компонент не более чем директива с шаблоном. Когда мы говорим, что компоненты являются строительными блоками приложений Angular, мы фактически говорим, что директивы являются строительными блоками приложений Angular.
Основной обзор
По сути, директива — это функция, которая выполняется всякий раз, когда компилятор Angular находит ее в DOM. Угловые директивы используются для расширения возможностей HTML, придав ему новый синтаксис. У каждой директивы есть имя — либо одно из предопределенных угловых, например, ng-repeat
, либо пользовательское, которое можно вызывать как угодно. И каждая директива определяет, где ее можно использовать: в element
, attribute
, class
или comment
.
По умолчанию, начиная с версии Angular 2 и далее, директивы Angular разделены на три разных типа:
Компоненты
Как мы видели ранее, компоненты — это просто директивы с шаблонами. Под капотом они используют директиву API и дают нам более понятный способ их определения.
Два других типа директив не имеют шаблонов. Вместо этого они специально приспособлены для манипуляций с DOM.
Директивы атрибутов
Директивы атрибутов управляют DOM, изменяя его поведение и внешний вид.
Мы используем директивы атрибутов для применения условного стиля к элементам, отображения или скрытия элементов или динамического изменения поведения компонента в соответствии с изменяющимся свойством.
Структурные директивы
Они специально предназначены для создания и уничтожения элементов DOM.
Некоторые директивы атрибутов — например, hidden
, который показывает или скрывает элемент — в основном поддерживают DOM как есть. Но структурные директивы Angular гораздо менее дружественны для DOM, так как они добавляют или полностью удаляют элементы из DOM. Поэтому, используя их, мы должны быть очень осторожны, так как мы на самом деле меняем структуру HTML.
Использование существующих угловых директив
Использовать существующие директивы в Angular довольно просто, и если вы писали приложение Angular в прошлом, я уверен, что вы использовали их. Директива ngClass
является хорошим примером существующей директивы атрибута Angular:
<p [ngClass]="{'blue'=true, 'yellow'=false}"> Angular Directives Are Cool! </p> <style> .blue{color: blue} .yellow{color: yellow} </style>
Итак, используя директиву ngClass
в приведенном ниже примере, мы фактически добавляем blue
класс к нашему абзацу и явно не добавляем yellow
. Поскольку мы меняем внешний вид класса, а не изменяем фактическую структуру HTML, это явно директива атрибута. Но Angular также предлагает ngIf
структурные директивы, такие как ngIf
:
@Component({ selector: 'ng-if-simple', template: ` <button (click)="show = !show">{{show ? 'hide' : 'show'}}</button> show = {{show}} <br> <div *ngIf="show">Text to show</div> ` }) class NgIfSimple { show: boolean = true; }
В этом примере мы используем директиву ngIf
для добавления или удаления текста с помощью кнопки. В этом случае затрагивается сама структура HTML, поэтому это явно структурная директива.
Для полного списка доступных угловых директив, мы можем проверить официальную документацию .
Как мы видели, использование угловых директив довольно просто. Настоящая сила угловых директив заключается в способности создавать свои собственные. Angular предоставляет чистый и простой API для создания пользовательских директив, и об этом мы рассмотрим в следующих разделах.
Создание директивы атрибута
Создание директивы похоже на создание компонента. Но в этом случае мы используем декоратор @Directive
. В нашем примере мы создадим директиву my-error-directive, которая выделит красным цветом фон элемента для обозначения ошибки.
В нашем примере мы будем использовать пакет быстрого запуска Angular 2 . Нам просто нужно клонировать репозиторий, затем запустить npm install
и npm start
. Это предоставит нам шаблонное приложение, которое мы можем использовать для экспериментов. Мы будем строить наши примеры поверх этого шаблона.
Начнем с создания файла с именем app.myerrordirective.ts
в папке src/app
и добавления в него следующего кода:
import {Directive, ElementRef} from '@angular/core'; @Directive({ selector:'[my-error]' }) export class MyErrorDirective{ constructor(elr:ElementRef){ elr.nativeElement.style.background='red'; } }
После импорта Directive
из @angular/core
мы можем ее использовать. Во-первых, нам нужен селектор, который дает имя директиве. В этом случае мы называем это my-error
.
В соответствии с передовой практикой мы всегда используем префикс при именовании наших угловых директив. Таким образом, мы обязательно избежим конфликтов с любыми стандартными атрибутами HTML. Мы также не должны использовать префикс ng
. Он используется Angular, и мы не хотим путать наши пользовательские директивы Angular с предопределенными Angular. В этом примере наш префикс my-
.
Затем мы создали класс MyErrorDirective
. Чтобы получить доступ к любому элементу нашей DOM, нам нужно использовать ElementRef
. Поскольку он также принадлежит пакету @angular/core
, его просто импортировать вместе с Directive
и использовать.
Затем мы добавили код, чтобы фактически выделить конструктор нашего класса.
Чтобы иметь возможность использовать эту недавно созданную директиву, нам нужно добавить ее в объявления в файле app.module.ts
:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { MyErrorDirective } from './app.myerrordirective'; import { AppComponent } from './app.component'; @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent, MyErrorDirective ], bootstrap: [ AppComponent ] }) export class AppModule { }
Наконец, мы хотим использовать директиву, которую мы только что создали. Для этого перейдем к файлу app.component.ts
и добавим следующее:
import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: `<h1 my-error>Hello {{name}}</h1>`, }) export class AppComponent { name = 'Angular'; }
Окончательный результат выглядит примерно так:
Создание структурной директивы
В предыдущем разделе мы увидели, как создать директиву атрибута, используя Angular. Подход к созданию структурного поведения точно такой же. Мы создаем новый файл с кодом для нашей директивы, затем добавляем его в объявления и, наконец, используем его в нашем компоненте.
Для нашей структурной директивы мы реализуем копию директивы ngIf
. Таким образом, мы не только будем реализовывать директиву, но и взглянем на то, как директивы Angular обрабатывают вещи за кулисами.
Давайте начнем с нашего файла app.mycustomifdirective.ts
:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[myCustomIf]' }) export class MyCustomIfDirective { constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { } @Input() set myCustomIf(condition: boolean) { if (condition) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } } }
Как мы видим, для этого мы используем несколько разных типов импорта, в основном: Input
, TemplateRef
и ViewContainerRef
. Декоратор Input
используется для передачи данных компоненту. TemplateRef
используется для создания встроенных представлений . Внедренное представление представляет часть макета, которая будет отображаться, и она связана с шаблоном. Наконец, ViewContainerRef
— это контейнер, к которому можно присоединить одно или несколько представлений. Вместе эти компоненты работают следующим образом:
Директивы получают доступ к контейнеру представления путем внедрения
ViewContainerRef
. Встроенные представления создаются и присоединяются к контейнеру представления путем вызоваViewContainerRef
createEmbeddedView
и передачи шаблона. Мы хотим использовать шаблон, к которому присоединена наша директива, поэтому мы передаем вставленныйTemplateRef
. — от Rangle.io’s Angular 2 Training
Далее мы добавляем его в наши деклараторы:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { MyErrorDirective } from './app.myerrordirective'; import { MyCustomIfDirective } from './app.mycustomifdirective'; import { AppComponent } from './app.component'; @NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent, MyErrorDirective, MyCustomIfDirective ], bootstrap: [ AppComponent ] }) export class AppModule { }
И мы используем это в нашем компоненте:
import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: `<h1 my-error>Hello {{name}}</h1> <h2 *myCustomIf="condition">Hello {{name}}</h2> <button (click)="condition = !condition">Click</button>`, }) export class AppComponent { name = 'Angular'; condition = false; }
Такой подход, обеспечиваемый структурными директивами, может быть очень полезным, например, когда нам приходится показывать разную информацию для разных пользователей на основе их разрешений. Например, администратор сайта должен видеть и редактировать все, а обычный пользователь — нет. Если мы загрузим личную информацию в DOM, используя директиву атрибута, обычный пользователь и все пользователи в этом отношении будут иметь к ней доступ.
Угловые Директивы: Атрибут против Структурных
Мы рассмотрели атрибуты и структурные директивы. Но когда мы должны использовать один или другой?
Ответ может ввести в заблуждение, и мы можем использовать неправильный ответ только потому, что он решает наши проблемы. Но есть простое правило, которое может помочь нам выбрать правильное. По сути, если элемент, имеющий директиву, будет по-прежнему полезен в DOM, когда DOM не виден, то мы обязательно должны его сохранить. В этом случае мы используем директиву атрибута наподобие hidden
. Но если элемент бесполезен, то мы должны удалить его. Тем не менее, мы должны быть осторожны, чтобы избежать некоторых распространенных ошибок. Мы должны избегать ловушек всегда скрывать элементы только потому, что это проще. Это сделает DOM намного более сложным и, вероятно, повлияет на общую производительность. Также следует избегать ловушек, связанных с постоянным удалением и воссозданием элементов. Это определенно чище, но происходит за счет производительности.
В общем, каждый случай должен быть тщательно проанализирован, потому что идеальным решением всегда является то, которое оказывает наименьшее общее влияние на структуру, поведение и производительность вашего приложения. Это решение может быть либо директивами атрибутов, структурными директивами, либо, в наиболее распространенном сценарии, компромиссом между ними.
Вывод
В этой статье мы рассмотрели директивы Angular, ядро приложений Angular. Мы рассмотрели различные типы директив и увидели, как создавать собственные, которые соответствуют нашим потребностям.
Я надеюсь, что эта статья смогла заставить вас работать с угловыми директивами. Если у вас есть какие-либо вопросы, не стесняйтесь использовать раздел комментариев ниже.