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.
Fue útil para mí ver esta opción basada en una directiva, pero encontré la que se ajustaba a mis necesidades en función de los componentes: https://www.ag-grid.com/ag-grid-angular-aot-dynamic-components/
¡Feliz codificación!
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);
});
});
}
}
Espero que te ayude!