Директивы с параметрами

Директивы в Angular играют ключевую роль в расширении функциональности приложений, позволяя манипулировать DOM-элементами и их поведением. В отличие от простых структурных и атрибутивных директив, которые используются для добавления или изменения элементов в шаблонах, директивы с параметрами обладают возможностью динамически изменять поведение этих элементов с учётом переданных данных.

Структура директивы с параметрами

Для того чтобы создать директиву с параметрами, нужно использовать декоратор @Directive, а также определить её логику в классе. Параметры, которые директива принимает, передаются через входные свойства директивы. В Angular для этого используются декораторы @Input().

import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  @Input() appHighlight: string;

  constructor(private el: ElementRef, private renderer: Renderer2) {}

  ngOnChanges() {
    this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', this.appHighlight);
  }
}

В данном примере директива HighlightDirective изменяет цвет фона элемента в зависимости от переданного параметра appHighlight. Этот параметр задается в шаблоне, где используется данная директива.

Использование директивы с параметрами

После того как директива создана, её можно применять в шаблонах Angular-компонентов. Параметры директивы передаются через атрибуты, которые должны быть указаны в соответствующих элементах HTML.

<p [appHighlight]="'yellow'">Этот текст будет выделен желтым.</p>

В данном примере директива appHighlight меняет фон элемента на желтый, потому что параметр 'yellow' передан в директиву.

Работа с несколькими параметрами

Если необходимо передавать несколько параметров в директиву, можно использовать несколько входных свойств. В этом случае каждый параметр будет указывать на отдельную переменную в классе директивы.

import { Directive, Input, ElementRef, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appBorder]'
})
export class BorderDirective {
  @Input() appBorderColor: string;
  @Input() appBorderWidth: string;

  constructor(private el: ElementRef, private renderer: Renderer2) {}

  ngOnChanges() {
    this.renderer.setStyle(this.el.nativeElement, 'border', `${this.appBorderWidth} solid ${this.appBorderColor}`);
  }
}

В этом примере директива BorderDirective принимает два параметра: цвет и ширину рамки. При изменении этих значений стили элемента будут обновляться.

<div [appBorderColor]="'blue'" [appBorderWidth]="'2px'">С этим элементом будет работать директива с несколькими параметрами.</div>

Динамическое обновление параметров

В случае, если параметры изменяются в ходе работы приложения, директива автоматически обновляет свой стиль благодаря жизненному циклу ngOnChanges. Этот хук вызывается каждый раз, когда значения входных свойств изменяются. Это позволяет директивам реагировать на изменения данных и динамически обновлять свой интерфейс.

Параметры с дефолтными значениями

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

import { Directive, Input, ElementRef, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appTextColor]'
})
export class TextColorDirective {
  @Input() appTextColor: string = 'black';  // Дефолтное значение

  constructor(private el: ElementRef, private renderer: Renderer2) {}

  ngOnChanges() {
    this.renderer.setStyle(this.el.nativeElement, 'color', this.appTextColor);
  }
}

В случае, если параметр appTextColor не передан, текст будет окрашен в черный цвет по умолчанию.

<p appTextColor>Текст будет черным, так как параметр не передан.</p>
<p [appTextColor]="'green'">Этот текст будет зеленым.</p>

Параметры с изменяемыми значениями

Если параметр директивы представляет собой сложный объект, например, массив или объект, его можно изменить и передать новые значения. Важно помнить, что Angular будет отслеживать только изменения ссылок на объекты, а не их внутреннее содержимое. Поэтому для эффективной работы с такими параметрами следует использовать глубокие изменения или технику отслеживания изменений.

import { Directive, Input, ElementRef, Renderer2, OnChanges, SimpleChanges } from '@angular/core';

@Directive({
  selector: '[appStyleChanges]'
})
export class StyleChangesDirective implements OnChanges {
  @Input() appStyleChanges: { color?: string; fontSize?: string };

  constructor(private el: ElementRef, private renderer: Renderer2) {}

  ngOnChanges(changes: SimpleChanges) {
    if (changes['appStyleChanges']) {
      const { color, fontSize } = this.appStyleChanges;
      if (color) {
        this.renderer.setStyle(this.el.nativeElement, 'color', color);
      }
      if (fontSize) {
        this.renderer.setStyle(this.el.nativeElement, 'fontSize', fontSize);
      }
    }
  }
}

В этом примере директива StyleChangesDirective принимает объект с параметрами color и fontSize. При изменении этого объекта стили элемента автоматически обновляются.

<p [appStyleChanges]="{color: 'blue', fontSize: '20px'}">Этот текст будет синим и крупным.</p>

Важность правильной обработки параметров

При работе с директивами с параметрами важно учитывать особенности жизненного цикла Angular. Особенно это касается асинхронных изменений и обновлений данных. Чтобы избежать лишних перерисовок и ненужных вычислений, можно использовать различные стратегии оптимизации, такие как ngOnChanges, ngDoCheck и другие хуки жизненного цикла, которые позволяют точно контролировать, когда и как следует обновлять параметры и состояние директивы.

Заключение

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