4

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.

David Gonzalez
  • 655
  • 3
  • 6
  • 25
  • *Check the [Equivalent of $compile in Angular 2](http://stackoverflow.com/a/37044960/1679310) there is a working plunker.. it could give some ideas* – Radim Köhler Jun 23 '16 at 08:32
  • Can you please add some example code of the element where you want to add the dynamically added component to better explain what the problem is with `ViewContainerRef.createComponent()` – Günter Zöchbauer Jun 23 '16 at 08:37
  • That would work if the containers were i am adding my components were existing on init and were fix. But they are created on a button press. And can be named "componentx" were x is the number of container already existing. So how to do "@ViewChild" of something not existing onInit and of something with a variable selector. – David Gonzalez Jun 23 '16 at 08:38
  • I added some code and more explanations. – David Gonzalez Jun 23 '16 at 08:54
  • What does `container.getElement()` return? – Günter Zöchbauer Jun 23 '16 at 08:58
  • According to the documentation of GoldenLayout :`Returns the container's inner element as a jQuery element` – David Gonzalez Jun 23 '16 at 09:00

0 Answers0