我有几个组件装饰器声明,我在每个组件上重复,例如:
@Component({
moduleId: module.id,
directives: [BootstrapInputDirective]
})
如何将这些声明应用于我的所有组件?我试图用这个装饰器创建一个基类,并用它扩展其他类,但基类装饰似乎不适用于派生类。
答案 0 :(得分:22)
@Component
是装饰者。这意味着它通过添加一些利用reflect-metadata库的元数据来处理它所应用的类。 Angular2不会在父类中查找元数据。因此,不能在父类上使用装饰器。
关于BootstrapInputDirective
指令,您可以将其定义为平台指令。这样,您每次都不需要将其包含在组件的directives
属性中。
以下是一个示例:
(...)
import {PLATFORM_DIRECTIVES} from 'angular2/core';
bootstrap(AppComponent, [
provide(PLATFORM_DIRECTIVES, {useValue: [BootstrapInputDirective], multi:true})
]);
修改强>
是的,您可以创建自己的装饰器来实现这一点。这是一个示例:
export function CustomComponent(annotation: any) {
return function (target: Function) {
var parentTarget = annotation.parent;
delete annotation.parent;
var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);
var parentAnnotation = parentAnnotations[0];
Object.keys(parentAnnotation).forEach(key => {
if (isPresent(parentAnnotation[key])) {
annotation[key] = parentAnnotation[key];
}
});
var metadata = new ComponentMetadata(annotation);
Reflect.defineMetadata('annotations', [ metadata ], target);
}
}
CustomComponent
装饰器将以这种方式使用:
@Component({
template: `
<div>Test</div>
`
})
export class AbstractComponent {
}
@CustomComponent({
selector: 'sub',
parent: AbstractComponent
})
export class SubComponent extends AbstractComponent {
}
请注意,我们需要提供父类作为装饰器的输入,因为我们可以在装饰器中找到这个父类。只有该类的原型,但元数据应用于类而不是关联原型上的反射元数据。
<强> EDIT2 强>
感谢Nitzam的回答,这是一个改进:
export function CustomComponent(annotation: any) {
return function (target: Function) {
var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);
var parentAnnotation = parentAnnotations[0];
Object.keys(parentAnnotation).forEach(key => {
if (isPresent(parentAnnotation[key])) {
annotation[key] = parentAnnotation[key];
}
});
var metadata = new ComponentMetadata(annotation);
Reflect.defineMetadata('annotations', [ metadata ], target);
}
}
不需要parent
属性来引用自定义装饰器中的父类。
请参阅此plunkr:https://plnkr.co/edit/ks1iK41sIBFlYDb4aTHG?p=preview。
看到这个问题:
答案 1 :(得分:8)
在最新版本的Angular之后,ComponentMetadata类并不像这里的少数成员所指出的那样可用。
这就是我实现CustomComponent以使其工作的方式:
export function CustomComponent(annotation: any) {
return function (target: Function) {
let parentTarget = Object.getPrototypeOf(target.prototype).constructor;
let parentAnnotations = Reflect.getOwnMetadata('annotations', parentTarget);
let parentAnnotation = parentAnnotations[0];
Object.keys(annotation).forEach(key => {
parentAnnotation[key] = annotation[key];
});
};
}
希望它有所帮助!
修改强>: 前面的代码块,即使它工作,它也会覆盖扩展类的原始元数据。在下面找到它的增强版本,允许您在不修改基类的情况下拥有多个继承和覆盖。
export function ExtendComponent(annotation: any) {
return function (target: Function) {
let currentTarget = target.prototype.constructor;
let parentTarget = Object.getPrototypeOf(target.prototype).constructor;
let parentAnnotations = Reflect.getOwnMetadata('annotations', parentTarget);
Reflect.defineMetadata('annotations', [Object.create(parentAnnotations[0])], currentTarget);
let currentAnnotations = Reflect.getOwnMetadata('annotations', currentTarget);
Object.keys(annotation).forEach(key => {
currentAnnotations[0][key] = annotation[key];
});
};
}
答案 2 :(得分:6)
如果有人正在寻找更新的解决方案,那么Thierry Templier的答案非常完美。除了ComponentMetadata
已被弃用。使用Component
代替我工作。
完整的Custom Decorator CustomDecorator.ts
文件如下所示:
import 'zone.js';
import 'reflect-metadata';
import { Component } from '@angular/core';
import { isPresent } from "@angular/platform-browser/src/facade/lang";
export function CustomComponent(annotation: any) {
return function (target: Function) {
var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);
var parentAnnotation = parentAnnotations[0];
Object.keys(parentAnnotation).forEach(key => {
if (isPresent(parentAnnotation[key])) {
// verify is annotation typeof function
if(typeof annotation[key] === 'function'){
annotation[key] = annotation[key].call(this, parentAnnotation[key]);
}else if(
// force override in annotation base
!isPresent(annotation[key])
){
annotation[key] = parentAnnotation[key];
}
}
});
var metadata = new Component(annotation);
Reflect.defineMetadata('annotations', [ metadata ], target);
}
}
然后将其导入新的组件sub-component.component.ts
文件并使用@CustomComponent
代替@Component
,如下所示:
import { CustomComponent } from './CustomDecorator';
import { AbstractComponent } from 'path/to/file';
...
@CustomComponent({
selector: 'subcomponent'
})
export class SubComponent extends AbstractComponent {
constructor() {
super();
}
// Add new logic here!
}
答案 3 :(得分:5)
以防您正在寻找isPresent功能:
function isPresent(obj: any): boolean {
return obj !== undefined && obj !== null;
}
答案 4 :(得分:1)
您可以在引导功能中全局提供服务,例如:
bootstrap(AppComponent, [HTTP_PROVIDERS, provide(SharedService, {useValue: sharedService})]);
其中sharedService是您导入的服务。