Decorador de componente extendido con decorador de clase base

Tengo varias declaraciones de decorador de componentes que repito en cada componente, por ejemplo:

@Component({ moduleId: module.id, directives: [BootstrapInputDirective] }) 

¿Cómo puedo aplicar estas declaraciones a todos mis componentes? Traté de crear una clase base con este decorador y extender otras clases con él, pero las decoraciones de la clase base no parecen aplicarse a las clases derivadas.

@Component es un decorador. Esto significa que maneja la clase a la que se aplica al agregar algunos datos de metadatos aprovechando la biblioteca reflect-metadata. Angular2 no busca metadatos en las clases principales. Por esta razón, no es posible usar decoradores en clases principales.

Con respecto a la directiva BootstrapInputDirective , podría definirla como una plataforma única. De esta forma, no necesitaría incluirlo cada vez en el atributo de directives de sus componentes.

Aquí hay una muestra:

 (...) import {PLATFORM_DIRECTIVES} from 'angular2/core'; bootstrap(AppComponent, [ provide(PLATFORM_DIRECTIVES, {useValue: [BootstrapInputDirective], multi:true}) ]); 

Editar

Sí, podrías crear tu propio decorador para implementar esto. Aquí hay una muestra:

 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); } } 

El decorador CustomComponent se usará de esta manera:

 @Component({ template: ` 
Test
` }) export class AbstractComponent { } @CustomComponent({ selector: 'sub', parent: AbstractComponent }) export class SubComponent extends AbstractComponent { }

Tenga en cuenta que debemos proporcionar la clase padre como entrada del decorador, ya que podemos encontrar esta clase principal dentro del decorador. Solo el prototipo de esta clase, excepto los metadatos, se aplica a la clase y no al prototipo asociado mediante reflect-metadata.

Edit2

Gracias a la respuesta de Nitzam, aquí hay una mejora:

 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); } } 

No es necesario que un atributo parent haga referencia a la clase principal en el decorador personalizado.

Ver este plunkr: https://plnkr.co/edit/ks1iK41sIBFlYDb4aTHG?p=preview .

Ver esta pregunta:

  • Cómo obtener la clase padre en tiempo de ejecución

Después de las últimas versiones de Angular, la clase ComponentMetadata no está disponible, como señalaron algunos miembros aquí.

Así es como he implementado CustomComponent para que funcione:

 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]; }); }; } 

¡Espero eso ayude!

EDITAR : el fragmento anterior de código, incluso si funciona, anula los metadatos originales de la clase extendida. Encuentre a continuación una versión mejorada de la misma, que le permite tener múltiples herencias y anulaciones sin modificar la clase base.

 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]; }); }; 

}

Si alguien está buscando una solución actualizada, la respuesta de Thierry Templier es bastante perfecta. Excepto que ComponentMetadata ha quedado en desuso. El uso de Component lugar funcionó para mí.

El archivo Custom Decorator CustomDecorator.ts completo se ve así:

 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); } } 

Luego, impórtelo en su nuevo componente sub-component.component.ts y use @CustomComponent lugar de @Component como este:

 import { CustomComponent } from './CustomDecorator'; import { AbstractComponent } from 'path/to/file'; ... @CustomComponent({ selector: 'subcomponent' }) export class SubComponent extends AbstractComponent { constructor() { super(); } // Add new logic here! } 

En caso de que esté buscando la función isPresent:

function isPresent(obj: any): boolean { return obj !== undefined && obj !== null; }

Puede proporcionar servicios de forma global en su función de arranque como:

 bootstrap(AppComponent, [HTTP_PROVIDERS, provide(SharedService, {useValue: sharedService})]); 

donde sharedService es su servicio importado.