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.
@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.
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.
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:
@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
Thanks to Nitzam's answer, here is an improvment:
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.
See this plunkr: https://plnkr.co/edit/ks1iK41sIBFlYDb4aTHG?p=preview.
See this question:
Just in case you are looking for isPresent function:
function isPresent(obj: any): boolean {
return obj !== undefined && obj !== null;
}
After latest releases of Angular, the ComponentMetadata class isn't available as pointed out by few members here.
This is how I've implemented the CustomComponent to make it work:
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];
});
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With