I am trying to dynamically load components into another. Before beta 14 we used DynamicComponentLoader.loadIntoLocation
but as it is now deprecated and removed we are looking for the new way of instantiating components.
I saw the ComponentResolver and ViewContainerRef
way of, but i can not use directly that solution.
We are using GoldenLayout to implement docking functions to our webpage. So the containers created by goldenlayout are dynamically injected into the DOM by jQuery and so i cannot use the @ViewChild
to get a reference of the ViewContainerRef
and use it with the new method.
I found a way of doing what i wanted with the DynamicComponentLoader.loadAsRoot
:
container.getElement().html(`<div id='component${Component.componentCount}'></div>`);
this.dynamicComponentLoader.loadAsRoot(Component,selector,this.injector)
.then((componentRef:ComponentRef<Component>)=>{
container.compRef = componentRef
(<any>this.applicationRef)._loadComponent(componentRef);
if(componentRef.instance.ngOnChanges){
componentRef.instance.ngOnChanges();
}
return componentRef;
});
Where the selector is the one we dynamically added with the .getElement().html()
.
But i do not like this way of implementing it as i am using the DynamicComponentLoader
which is now deprecated, and also because of the
(<any>this.applicationRef)._loadComponent(componentRef);
which is use to "fool" the transpiler in order to use _loadComponent()
which is not exposed by the ApplicationRef
.
So i am basically looking for a way to dynamically retrieve a ViewContainerRef
during runtime. Or a more elegant way of implementing what i did (i.e without deprecated or hidden functions).
Please tell me if i did not correctly explain myself and if anything is not clear.
Thank you in advance.
David.
Edit:
My Directive working right now:
import { Directive,Component, OnInit ,ApplicationRef, ElementRef,ComponentRef,Type,Input,SimpleChange,DynamicComponentLoader,Injector} from '@angular/core';
import { TestaComponent } from '../testa/testa.component'
import { TestbComponent} from '../testb/testb.component'
declare var GoldenLayout : any;
declare var $ : JQueryStatic;
@Directive({
selector: '[goldenlayout]',
})
export class GoldenLayoutComponent{
@Input() glConfig : any;
private myLayout : any;
static componentCount : number = 0;
constructor(private applicationRef:ApplicationRef, private elementRef : ElementRef,private dynamicComponentLoader:DynamicComponentLoader,private injector: Injector) {}
ngOnInit(){
let goldenConfig = this.glConfig || {
content:[{
type:'row',
content:[
]
}]
};
this.glConfig = goldenConfig;
this.myLayout = new GoldenLayout(goldenConfig,this.elementRef.nativeElement);
this.myLayout.registerComponent('TestAComponent',(container,componentState)=>{
container.getElement().html("<div id='component" + GoldenLayoutComponent.componentCount +"'></div>");
this.dynamicComponentLoader.loadAsRoot(TestaComponent,"#component"+GoldenLayoutComponent.componentCount,this.injector)
.then((componentRef:ComponentRef<TestaComponent>)=>{
container.compRef = componentRef;
(<any>this.applicationRef)._loadComponent(componentRef);
componentRef.instance["buttonName"]= componentState.buttonName;
if(componentRef.instance.ngOnChanges){
componentRef.instance.ngOnChanges();
}
return componentRef;
});
GoldenLayoutComponent.componentCount ++;
});
this.myLayout.registerComponent('TestBComponent',(container,componentState)=>{
container.getElement().html("<div id='component" + GoldenLayoutComponent.componentCount +"'></div>");
this.dynamicComponentLoader.loadAsRoot(TestbComponent,"#component"+GoldenLayoutComponent.componentCount,this.injector)
.then((componentRef:ComponentRef<TestbComponent>)=>{
container.compRef = componentRef;
(<any>this.applicationRef)._loadComponent(componentRef);
componentRef.instance["color"]= componentState.color;
if(componentRef.instance.ngOnChanges){
componentRef.instance.ngOnChanges();
}
return componentRef;
});
GoldenLayoutComponent.componentCount ++;
});
this.myLayout.init();
this.myLayout.on("itemDestroyed",(item)=>{
if (item.container && item.container.compRef){
this.disposeComp(item.container.compRef);
}
});
this.myLayout.on("windowClosed",(item)=>{
this.applicationRef.tick();
});
}
disposeComp(comp : ComponentRef<any>){
if (comp.instance && comp.instance.ngOnDestroy){
comp.instance.ngOnDestroy();
(<any>this.applicationRef)._unloadComponent(comp);
}
}
addComponent(name : string,option: string){
let newComp : any;
if(name == "TestAComponent"){
newComp = {
type: 'component',
componentName: name,
componentState: {buttonName : option}
};
}else{
newComp = {
type: 'component',
componentName: name,
componentState: {color : option}
};
}
this.myLayout.root.contentItems[ 0 ].addChild(newComp);
}
}
My html:
<div style="margin:5px;">
<button (click)='addComponent("TestAComponent",option)'>Spawn test a</button>
<button (click)='addComponent("TestBComponent",option)'>Spawn test b</button>
<input [(ngModel)]="option" type="text"/>
</div>
<div style="height:800px;" goldenlayout></div>
The probleme is that the element where i want to add my component is created with container.getElement().html(
);
when i call the addComponent function.
At first my DOM is like this :
<div>
<button>Spawn test a</button>
<button>Spawn test b</button>
<input/>
</div>
<div goldenlayout>
<div class="lm_goldenlayout lm_item lm_root"></div>
<div class="lm_item lm_row"></div>
</div>
Then i Spawn a container with one of the buttons. And my DOM loooks like this:
<div>
<button>Spawn test a</button>
<button>Spawn test b</button>
<input/>
</div>
<div goldenlayout>
<div class="lm_goldenlayout lm_item lm_root"></div>
<div class="lm_item lm_row">
...
<div class="lm_items">
<div class="lm_content">
<div id="component0" _nghost-mip-3="">
<div _ngcontent-mip-3="">
<button _ngcontent-mip-3="">not set</button>
</div>
</div>
</div>
</div>
...
</div>
</div>
What i load dynamically is what is spawned in the component0
div and all the element with lm_
are created by goldenlayout.