Подтвердить что ты не робот

Доступ к мета-аннотации внутри класса (TypeScript)

Когда я аннотирую класс с метаданными в TypeScript, например. для создания компонента Angular2, могу ли я получить доступ к метаданным внутри этого класса?

import {Component} from 'angular2/core';

@Component({
    selector: 'app',
    templateUrl: '/app/components/app/app.component.html'
})
export class AppComponent {

    // can I access 'templateUrl' from above annotation here?

}
4b9b3361

Ответ 1

В новой угловой версии вы должны использовать собственный объект Reflect. Нет поддержки IE11, хотя:

const annotations = Reflect.getOwnPropertyDescriptor(MyModule, '__annotations__').value;

Прочитайте ответ Джеффа Фэрли ниже о том, что делать с данными после этого

Вы можете увидеть аннотацию/декоратор как обычный вызов функции. Для этой функции объект "Класс/Функция" (не экземпляр) отправляется в первом параметре, а параметры (метаданные) - во втором аргументе.

Однако от реализации этой функции зависит, добавится ли что-нибудь, например, к прототипу класса (плохая практика/разоблачающее свойство). Компилятор TypeScript и Angular2 делают вещи по-разному, хотя.

Они используют функции __decorate и __metadata, которые генерируются компилятором TypeScript. Данные добавляются с помощью функции Object.defineProperty(). Ответственный за это пакет - Reflect. (который под капотом использует Object.defineProperty() в сочетании с WeakMap).

Функция Reflect.defineMetadata() используется для установки аннотаций и для получения их очевидного Reflect.getMetadata().

TL;DR;

  • Чтобы получить аннотации от класса/компонента в angular2, вы должны использовать:

    Reflect.getMetadata('annotations', ComponentClass); //@Component({}), @Pipe({}), ...
    
  • Чтобы получить аннотации от параметров конструктора в angular2, вы должны использовать:

    Reflect.getMetadata('parameters', ComponentClass); //@Inject()
    
  • Чтобы получить аннотации от свойства в классе в angular2, вы должны использовать:

    Reflect.getMetadata('propMetadata', ComponentClass); //@HostBinding(), @Input(), ...
    

Ответ 2

Ответ @PierreDuc не работает для меня в Angular 5. Я продолжил чтение (извините, я не могу найти все релевантные ссылки для обмена) и придумал следующее:

let decorator: Type<any>;
// you can cast the component class
decorator = (<any>AppComponent).__annotations__[0];
// or use with lodash.get
decorator = get(AppComponent, '__annotations__[0]');

//
// obviously, this is an array, and based on your app,
// you may have multiple decorators on one class, so iterate them
//


// if you want to just grab some data from the @Component:
const templateUrl: string = decorator.templateUrl;
// or from a @NgModule:
const declarations: Type<any>[] = decorator.declarations;


// or if you're trying to decide whether this is in fact a component:
if (decorator instanceof Component) {
  // do things
}
// or a provider
if (decorator instanceof Injectable) {
  // do different things
}
// or a module:
if (decorator instanceof NgModule) {
  // do things
}

// etc...

Я еще не обновил свое приложение до Angular 6, поэтому я не проверял, что это не изменилось.

Связанные с:

Ответ 3

Мы использовали это решение и выяснили, что AOT удаляет __annotations__ (который также используется по умолчанию для --prod), что делает его бесполезным для нас.

К счастью, если вы имеете дело с компонентами, есть альтернатива, которая работает в обоих и не использует строки:

const components = [ MyComponent ];
/*
  ...
  Anything that can be injected into
*/
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
  // For each component
  components.forEach(element => {
    // Get its resolved factory
    const factory = this.componentFactoryResolver.resolveComponentFactory(element);

    console.log('Factory:', factory);
    console.log('Selector:', factory.selector);
  });
}

Поскольку он имеет дело с правильными типами и интерфейсами, он также одобрен!