Декораторы и метаданные

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

Основные типы декораторов

  1. @Component Это самый часто используемый декоратор в Angular, который определяет компонент. Он добавляет метаданные, описывающие шаблон, стили и другие свойства компонента.

    Пример:

    @Component({
      selector: 'app-hero',
      templateUrl: './hero.component.html',
      styleUrls: ['./hero.component.css']
    })
    export class HeroComponent {
      name = 'Windstorm';
    }

    Здесь декоратор указывает, что HeroComponent — это Angular компонент с определённым шаблоном и стилями.

  2. @NgModule Декоратор @NgModule применяется к модулю, чтобы описать метаданные модуля. Он определяет, какие компоненты, директивы и пайпы являются частью модуля, а также какие сервисы и другие модули импортируются.

    Пример:

    @NgModule({
      declarations: [AppComponent],
      imports: [BrowserModule],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }

    В данном случае AppModule является корневым модулем приложения.

  3. @Injectable Декоратор @Injectable используется для сервисов, которые могут быть внедрены в другие компоненты или сервисы через механизмы инъекции зависимостей. Он позволяет Angular создать и предоставить экземпляр класса в нужный момент.

    Пример:

    @Injectable({
      providedIn: 'root',
    })
    export class HeroService {
      getHeroes() {
        return ['Windstorm', 'Magneta', 'Tornado'];
      }
    }

    В данном примере HeroService будет доступен для инъекции в компоненты и другие сервисы.

  4. @Directive Декоратор @Directive используется для создания директив в Angular. Директивы влияют на поведение элементов DOM, но не имеют собственного шаблона.

    Пример:

    @Directive({
      selector: '[appHighlight]'
    })
    export class HighlightDirective {
      constructor(private el: ElementRef) {
        el.nativeElement.style.backgroundColor = 'yellow';
      }
    }

    В этом примере директива меняет цвет фона элемента, к которому она применяется.

  5. @Input и @Output Эти декораторы используются для связывания данных между компонентами. @Input позволяет передавать данные от родительского компонента к дочернему, а @Output — для отправки событий из дочернего компонента в родительский.

    Пример:

    @Component({
      selector: 'app-child',
      template: `<button (click)="sendMessage()">Send</button>`
    })
    export class ChildComponent {
      @Output() messageEvent = new EventEmitter<string>();
    
      sendMessage() {
        this.messageEvent.emit('Hello Parent!');
      }
    }
    
    @Component({
      selector: 'app-parent',
      template: `<app-child (messageEvent)="receiveMessage($event)"></app-child>`
    })
    export class ParentComponent {
      receiveMessage(message: string) {
        console.log(message);  // Выведет 'Hello Parent!'
      }
    }

    В данном примере @Output создаёт событие, которое может быть обработано в родительском компоненте.

Метаданные и их значение

Метаданные, используемые в Angular, позволяют описать свойства и поведение компонентов, директив, пайпов и сервисов. Каждый декоратор сопровождается набором метаданных, которые Angular использует для создания и управления объектами приложения. Это позволяет фреймворку правильно обрабатывать жизненный цикл компонента, определять зависимости и связывать данные.

Метаданные могут быть явными или скрытыми. Явные метаданные задаются при помощи декораторов, а скрытые — это, например, стандартные настройки Angular, которые использует фреймворк для своих внутренних нужд (например, определения шаблона компонента).

Работа с метаданными через Reflect

Для работы с метаданными можно использовать API Reflect, который позволяет получить доступ к метаданным, добавленным к классу, свойствам или методам. Это особенно полезно при создании сложных библиотек и фреймворков, основанных на Angular.

Пример получения метаданных:

import { Reflect } from 'core-js';

class MyClass {
  @MyDecorator()
  method() {}
}

function MyDecorator() {
  return function(target: any, propertyKey: string) {
    const metadata = Reflect.getMetadata('custom:metadata', target, propertyKey);
    console.log(metadata); // Вывод метаданных
  };
}

Декораторы и инъекция зависимостей

Важной частью Angular является механизм инъекции зависимостей, который позволяет управлять жизненным циклом сервисов и компонентов. Использование декораторов @Injectable, @Inject, @Optional и других связано с управлением зависимостями, которые Angular автоматически внедряет в классы.

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

Пример инъекции зависимостей:

@Injectable({
  providedIn: 'root',
})
export class LoggingService {
  log(message: string) {
    console.log(message);
  }
}

@Component({
  selector: 'app-root',
  template: `<button (click)="logMessage()">Log Message</button>`,
})
export class AppComponent {
  constructor(private loggingService: LoggingService) {}

  logMessage() {
    this.loggingService.log('Hello from Angular!');
  }
}

В этом примере Angular автоматически создаст экземпляр LoggingService и инъецирует его в компонент AppComponent.

Заключение

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