【问题标题】:Access Meta-Annotation inside Class (TypeScript)访问类内的元注释(TypeScript)
【发布时间】:2016-03-31 15:09:59
【问题描述】:

当我在 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?

}

【问题讨论】:

  • 一般的答案是“视情况而定”,装饰器只是包装你的类的东西。它可以根据自己的选择公开或不公开属性。它没有必须让您访问它 - 事实上它可能会选择完全忽略传递给它的参数。

标签: typescript angular


【解决方案1】:

使用新的 Angular 版本,您应该使用原生的 Reflect 对象。虽然不支持 IE11:

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

阅读下面 Jeff Fairley 的回答,了解之后如何处理数据

您可以将注释/装饰器视为正常的函数调用。对于这个函数,“类/函数”对象(不是实例)在第一个参数中发送,参数(元数据)在第二个参数中。

但是,如果某些东西被添加到类的原型中(不好的做法/暴露属性),这取决于该函数的实现。不过,TypeScript 编译器和 Angular2 做的事情不同。

它们使用由 TypeScript 编译器生成的 __decorate 和 __metadata 函数。数据通过Object.defineProperty() 函数添加。负责这个的包是Reflect。 (在底层使用Object.defineProperty() 函数和WeakMap)。

函数Reflect.defineMetadata()用于设置注解,并获得明显的Reflect.getMetadata()。

TLDR;

  • 要从 angular2 中的类/组件中获取注释,您有 使用:

    Reflect.getMetadata('annotations', ComponentClass); //@Component({}), @Pipe({}), ...
    
  • 要从 angular2 中的构造函数参数中获取注解,您必须使用:

    Reflect.getMetadata('parameters', ComponentClass); //@Inject()
    
  • 要从 angular2 中的类中的属性中获取注释,您 必须使用:

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

【讨论】:

    【解决方案2】:

    @PierreDuc's answer 在 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,所以我没有验证这没有改变。

    相关:

    【讨论】:

    • 不错的答案!这确实是现在应该做的,虽然如果你不关心IE,你现在可以使用原生的Reflect。 Reflect.getOwnPropertyDescriptor(AppComponent, '__annotations__');
    • 此答案更新,使其成为公认的答案。有关更多详细信息,请参阅stackoverflow.com/questions/46937746/…
    【解决方案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);
      });
    }
    

    因为它处理正确的类型和接口,所以它也是 linter 批准的!

    【讨论】:

    • 应该注意,在所有可能的annotations 中,只有inputs、outputs、queries 和selector 可以从ComponentFactory 访问。
    • 不幸的是,这不适用于指令:Type passed in is not ComponentType, it does not have 'ɵcmp' property.
    【解决方案4】:

    正如 John Neuhaus 所说,angular 只是在编译时删除 annotations 属性。所以,我们可以只做ng serve --aot=false,现在你可能会发现当你做console.dir(AppComponent);时注释会出现

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-19
      • 2015-11-18
      • 2018-06-10
      • 1970-01-01
      • 1970-01-01
      • 2017-04-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多