tutorial component compile javascript angular typescript

javascript - compile - Cargue los componentes existentes dinĂ¡micamente Angular 2 Final Release



typescript tutorial (2)

Estoy tratando de cargar dinámicamente un componente en la versión final 2.0.0.

Usando RC5 estaba cargando usando el siguiente código:

Cree una directiva para cargar los controles:

import { CheckboxComponent, CheckboxListComponent,DatePickerComponent } from ''../components/''; @Directive({ selector: ''[ctrl-factory]'' }) export class ControlFactoryDirective implements OnChanges { @Input() model: any; constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) { } create(cp) { this.resolver.resolveComponent(cp) .then(factory => { const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); this.vcRef.createComponent(factory, 0, injector, []); let ch = this.vcRef.createComponent(factory, 0, injector, []).instance; ch.model = this.model; }); } ngOnChanges() { if (!this.model) return; switch (this.model.type) { case ''checkbox'': this.create(CheckboxComponent); break; case ''checkboxlist'': this.create(CheckboxListComponent); break; case ''datepicker'': this.create(DatePickerComponent); break; default: break; } } }

Luego cargué esa directiva en mi página así:

<div ctrl-factory *ngFor="let child of page.childrens" [model]="child"></div>

Pero después de actualizar de la versión final de rc5 a 2.0.0, el resolutor ya no existe, fue reemplazado por el compilador.

Encontré muchos lugares que mostraban cómo cargarlo usando diferentes códigos, pero todos eran demasiado complejos y no pude hacerlo funcionar.

Tome esto por ejemplo: ¿Cómo puedo usar / crear plantilla dinámica para compilar Componente dinámico con Angular 2.0?

Parece más específico para ese escenario, solo necesito cargar el componente y configurar un modelo llamado @Input.

Una cosa cuando estaba intentando tenía que crear dinámicamente un módulo para cada componente, luego agregarle el componente. Pero luego tuve problemas para decir que el componente se estaba configurando en más de un Módulo, intenta eliminarlo en algún lugar y no funciona.

La mayor parte del código que se muestra proviene de este enlace: http://blog.lacolaco.net/post/dynamic-component-creation-in-angular-2-rc-5/

E hizo un par de cambios.

Actualizar

Me las arreglo para que funcione, utilizando el siguiente enfoque:

El método de creación se ha cambiado a

private create(cp) { @NgModule({ imports: [BrowserModule, ControlsModule], declarations: [] }) class DynamicModule {} this.compiler.compileModuleAndAllComponentsAsync(DynamicModule) .then(({componentFactories}) => { const compFactory = componentFactories.find(x => x.componentType === cp); const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector); const cmpRef = this.vcRef.createComponent(compFactory, 0, injector, []); cmpRef.instance.model = this.model; }); }

En la mayoría de los lugares que he encontrado, establezca crear el Componente y configúrelo en DynamicModule, el problema es que cuando ya está declarando ese mismo componente en un módulo diferente, angular se va a quejar. La solución en mi caso fue importar mi ControlMódulo que tiene todos mis controles exportados.



Actualizar

NgComponentOutlet se introdujo en 4.0.0-beta.3 https://github.com/angular/angular/commit/8578682

Próximamente NgComponentOutlet

Veo dos opciones para hacer eso:

1) Utilizando ComponentFactoryResolver .

Utiliza la fábrica ya generada y el código se ve así:

constructor(private vcRef: ViewContainerRef, private resolver: ComponentFactoryResolver) { } create(comp) { const factory = this.resolver.resolveComponentFactory(comp); const compRef = this.vcRef.createComponent(factory); (<any>compRef).instance.model = this.model; }

En este caso, tenemos que definir el componente dinámico en las declarations y entryComponents propiedades de entryComponents dentro del decorador del módulo

@NgModule({ imports: [ BrowserModule ], declarations: [ AppComponent, DynamicComponent ], entryComponents: [DynamicComponent], bootstrap: [ AppComponent ] }) export class AppModule { }

2) Usando el compilador

En este caso, solo podemos ejecutar la compilación del módulo utilizando compiler.compileModuleAndAllComponentsAsync y luego buscar el componente de la matriz componentFactories. Su directiva podría verse así:

constructor(private vcRef: ViewContainerRef, private loader: DynamicLoaderService) { } create(comp) { this.loader.createComponentFactory(comp).then(factory => { const compRef = this.vcRef.createComponent(factory); (<any>compRef).instance.model = this.model; }) }

DynamicLoaderService es un servicio global que cargará y almacenará fábricas de componentes.

@Injectable() export class DynamicLoaderService { constructor(protected compiler: Compiler) {} private resolveCompHelper$ = new Subject<any>(); private cache = new Map<string, ComponentFactory<any> | number>(); public createComponentFactory(type: string) : Promise<ComponentFactory<any>> { let factory = this.cache.get(type); // if factory has been already loading if(factory === 1) { return new Promise((resolve) => { // waiting compilation of factory const subscriber = this.resolveCompHelper$.subscribe((data) => { if(type !== data.type) return; subscriber.unsubscribe(); resolve(data.factory); }); }); } // factory exists in cache if (factory) { return new Promise((resolve) => resolve(factory)); } const comp = typeMap[type]; // factory startes loading this.cache.set(type, 1); return new Promise((resolve) => { this.compiler.compileModuleAndAllComponentsAsync(createComponentModule(comp)) .then((moduleWithFactories: ModuleWithComponentFactories<any>) => { factory = moduleWithFactories.componentFactories .find(x => x.componentType === comp); this.cache.set(type, factory); this.resolveCompHelper$.next({ type, factory}); resolve(factory); }); }); } }

Ejemplo de Plunker

Espero que te ayude!