world versiones starter hello guide app typescript angular

typescript - versiones - Decorador de componentes extensible con decorador de clase base



install angular (5)

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? Intenté 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.


Después de los últimos lanzamientos de Angular, la clase ComponentMetadata no está disponible como lo señalan algunos miembros aquí.

Así es como he implementado el 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 de código anterior, incluso si funciona, anula los metadatos originales de la clase extendida. Encuentre a continuación una versión mejorada del mismo, 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]; }); };

}


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

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


Puede proporcionar servicios a nivel mundial en su función de arranque como:

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

donde sharedService es su servicio importado.


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

El archivo completo Custom Decorator CustomDecorator.ts tiene este aspecto:

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 a su nuevo componente sub-component.component.ts y use @CustomComponent lugar de @Component esta manera:

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


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

Con respecto a la directiva BootstrapInputDirective , puede definirla como una plataforma. De esta manera, 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ía crear su 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: ` <div>Test</div> ` }) export class AbstractComponent { } @CustomComponent({ selector: ''sub'', parent: AbstractComponent }) export class SubComponent extends AbstractComponent { }

Tenga en cuenta que debemos proporcionar la clase principal como entrada del decorador ya que podemos encontrar esta clase principal dentro del decorador. Solo el prototipo de esta clase pero los metadatos se aplican en la clase y no en el prototipo asociado por 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 padre en el decorador personalizado.

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

Ver esta pregunta: