Estensione del decoratore di componenti con decoratore di class base

Ho diverse dichiarazioni di decoratore di componenti che ripeto su ogni componente, ad esempio:

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

Come posso applicare queste dichiarazioni a tutti i miei componenti? Ho provato a creare una class base con questo decoratore e ad estenderne altre classi, ma le decorazioni di class base non sembrano essere applicabili alle classi derivate.

@Component è un decoratore. Ciò significa che gestisce la class su cui si applica aggiungendo alcuni dati di metadati sfruttando la libreria reflect-metadata. Angular2 non cerca i metadati sulle classi genitore. Per questo motivo, non è ansible utilizzare i decoratori sulle classi genitore.

Per quanto riguarda la direttiva BootstrapInputDirective , è ansible definirla come una piattaforma. In questo modo non avresti bisogno di includerlo ogni volta nell’attributo delle directives dei tuoi componenti.

Ecco un esempio:

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

modificare

Sì, potresti creare il tuo decoratore per implementarlo. Ecco un esempio:

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

Il decoratore CustomComponent sarà utilizzato in questo modo:

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

Si noti che è necessario fornire la class genitore come input del decoratore poiché possiamo scoprire questa class genitore all’interno del decoratore. Solo il prototipo di questa class, ma i metadati vengono applicati sulla class e non sul prototipo associato da reflect-metadata.

Edit2

Grazie alla risposta di Nitzam, ecco un miglioramento:

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

Non è necessario che un attributo parent faccia riferimento alla class genitore nel decoratore personalizzato.

Vedi questo plunkr: https://plnkr.co/edit/ks1iK41sIBFlYDb4aTHG?p=preview .

Vedi questa domanda:

  • Come ottenere la class genitore in fase di runtime

Dopo le ultime versioni di Angular, la class ComponentMetadata non è disponibile come indicato da alcuni membri qui.

Ecco come ho implementato CustomComponent per farlo funzionare:

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

Spero che sia d’aiuto!

EDIT : il pezzo precedente del codice, anche se funziona, sovrascrive i metadati originali della class estesa. Trova di seguito una versione avanzata di esso, che ti consente di avere più ereditarietà e sostituzioni senza modificare la class 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]; }); }; 

}

Se qualcuno sta cercando una soluzione aggiornata, la risposta di Thierry Templier è praticamente perfetta. Tranne che ComponentMetadata è stato deprecato. L’uso di Component invece ha funzionato per me.

Il file CustomDecorator.ts Custom Decorator completo ha il seguente aspetto:

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

Quindi importalo nel tuo nuovo componente sub-component.component.ts e usa @CustomComponent invece di @Component questo modo:

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

Nel caso in cui stiate cercando la funzione isPresent:

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

Puoi fornire servizi a livello globale nella tua funzione di bootstrap come:

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

dove sharedService è il tuo servizio importato.