I'm trying to read a list of components and dynamically creating them on my page. I'm using ComponentResolver for this and creating the components using the new @ViewChild way.
I have a MainComponent file that extends a ComponentCreator class. This ComponentCreator class is a base class which all other components can 'extend' and use to create their respective child components.
Here are the code snippets to make things clearer:
MainComponent.ts
export class MainComponent extends ComponentCreator {
@ViewChild("target", { read: ViewContainerRef }) target;
constructor(_componentResolver: ComponentResolver, metaService: MetaService) {
super(_componentResolver, metaService, "./src/app/meta.json", "MainComponent");
}
ComponentCreator.ts
export class ComponentCreator{
//declare variables
constructor(_componentResolver: ComponentResolver, _metaService: MetaService, _templateUrl: string, _templateName: string) {
this._componentResolver = _componentResolver;
this._templateUrl = _templateUrl;
this._metaService = _metaService;
this._templateName = _templateName;
}
ngAfterViewInit() {
//metaService gets the template. It's a json file in which all the child components of Main component are listed
this._metaService.getTemplate(this._templateUrl)
.subscribe(
_template => this._template = this.loadComponents(_template),
error => this._errorMessage = <any>error);
}
loadChildComponents(template) {
//Create components here
//'place' comes from the json file. It's Equal to target.
for(let component in jsonData)
this._componentResolver.resolveComponent(component).then((factory:ComponentFactory<any>)=> { this.cmpRef = this.place.createComponent(factory)});
}
}
The problem I'm facing is in the order of component creation. For example, I have 4 child components of which 2 are plain HTML tables and 2 are some charts that are drawn using d3. Even though I'm specifying the creation order as 1,2,3,4 ; the order of rendering is messed up. As they all load inside the 'target' div, the HTML tables are rendered fast and come before both the charts.
Is there any way to fix it or will I have to use separate divs for tables and charts so that the order remains the same?
I think the problem is that you call async code within a for()
loop which results in random order.
Use Promise.all()
to ensure they are executed one after the other
Promise.all(
jsonData.map(component =>
this._componentResolver.resolveComponent(component)
.then((factory:ComponentFactory<any>)=> {
this.cmpRef = this.place.createComponent(factory);
});
)
);
See also a similar example for the old DynamicComponentLoader
at https://github.com/angular/angular/issues/7854#issuecomment-203988888