typescript 使用基类装饰器扩展组件装饰器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36837421/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Extending component decorator with base class decorator
提问by dstr
I have several component decorator declarations that I repeat on every component, for example:
我有几个组件装饰器声明,我在每个组件上重复这些声明,例如:
@Component({
moduleId: module.id,
directives: [BootstrapInputDirective]
})
How can I apply these declarations to all my components? I tried to create a base class with this decorator and extend other classes with it but base class decorations doesn't seem to apply to derivative classes.
如何将这些声明应用于我的所有组件?我试图用这个装饰器创建一个基类并用它扩展其他类,但基类装饰似乎不适用于派生类。
回答by Thierry Templier
@Component
is a decorator. This means that it handles the class it applies on by adding some metadata data leveraging the reflect-metadata library. Angular2 doesn't look for metadata on parent classes. For this reason, it's not possible to use decorators on parent classes.
@Component
是一个装饰器。这意味着它通过添加一些利用反射元数据库的元数据来处理它所应用的类。Angular2 不会在父类上查找元数据。因此,不可能在父类上使用装饰器。
Regarding the BootstrapInputDirective
directive, you could define it as a platform one. This way you wouldn't need to include it each time into the directives
attribute of your components.
关于BootstrapInputDirective
指令,您可以将其定义为平台指令。这样你就不需要每次都将它包含到directives
组件的属性中。
Here is a sample:
这是一个示例:
(...)
import {PLATFORM_DIRECTIVES} from 'angular2/core';
bootstrap(AppComponent, [
provide(PLATFORM_DIRECTIVES, {useValue: [BootstrapInputDirective], multi:true})
]);
Edit
编辑
Yes, you could create your own decorator to implement this. Here is a sample:
是的,您可以创建自己的装饰器来实现这一点。这是一个示例:
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);
}
}
The CustomComponent
decorator will be used this way:
该CustomComponent
装饰将采用这种方式:
@Component({
template: `
<div>Test</div>
`
})
export class AbstractComponent {
}
@CustomComponent({
selector: 'sub',
parent: AbstractComponent
})
export class SubComponent extends AbstractComponent {
}
Note that we need to provide the parent class as input of the decorator since we can find out this parent class within the decorator. Only the prototype of this class but the metadata are applied on the class and not on the associated prototype by reflect-metadata.
请注意,我们需要提供父类作为装饰器的输入,因为我们可以在装饰器中找到这个父类。反射元数据仅将此类的原型和元数据应用于该类,而不应用于关联的原型。
Edit2
编辑2
Thanks to Nitzam's answer, here is an improvment:
感谢 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);
}
}
There is no need for a parent
attribute to reference the parent class in the custom decorator.
不需要parent
属性来引用自定义装饰器中的父类。
See this plunkr: https://plnkr.co/edit/ks1iK41sIBFlYDb4aTHG?p=preview.
请参阅此 plunkr:https://plnkr.co/edit/ks1iK41sIBFlYDb4aTHG ?p =preview 。
See this question:
看到这个问题:
回答by Guinnberg
After latest releases of Angular, the ComponentMetadata class isn't available as pointed out by few members here.
在最新版本的 Angular 之后,ComponentMetadata 类不可用,正如这里的少数成员所指出的那样。
This is how I've implemented the CustomComponent to make it work:
这是我实现 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];
});
};
}
Hope it helps!
希望能帮助到你!
EDIT: the previous chunk of code, even if it works, it overrides the original metadata of the extended class. Find below an enhanced version of it, allowing you to have multiple inheritances and overrides without modifying the base class.
编辑:前面的代码块,即使它有效,它也会覆盖扩展类的原始元数据。在下面找到它的增强版本,允许您在不修改基类的情况下进行多重继承和覆盖。
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];
});
};
}
}
回答by rhyneav
If anyone is looking for an updated solution, Thierry Templier's answer is pretty much perfect. Except that ComponentMetadata
has been deprecated. Using Component
instead worked for me.
如果有人正在寻找更新的解决方案,Thierry Templier 的回答非常完美。除非ComponentMetadata
已被弃用。使用Component
代替对我有用。
The full Custom Decorator CustomDecorator.ts
file looks like this:
完整的自定义装饰器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);
}
}
Then import it in to your new component sub-component.component.ts
file and use @CustomComponent
instead of @Component
like this:
然后将其导入到您的新组件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!
}
回答by dipcore
Just in case you are looking for isPresent function:
以防万一您正在寻找 isPresent 函数:
function isPresent(obj: any): boolean {
return obj !== undefined && obj !== null;
}
function isPresent(obj: any): boolean {
return obj !== undefined && obj !== null;
}
回答by eko
You can provide services globally in your bootstrap function like:
您可以在 bootstrap 函数中全局提供服务,例如:
bootstrap(AppComponent, [HTTP_PROVIDERS, provide(SharedService, {useValue: sharedService})]);
where sharedService is your imported service.
其中 sharedService 是您导入的服务。