Francis Ducharme
Francis Ducharme

Reputation: 4987

Dynamically create a component inside a dynamically created component

As the title says, I'm trying to create a component dynamically, inside a component that was also dynamically created.

I've got this class here

export class DefaultLayoutComponent {

    constructor(private cdRef: ChangeDetectorRef, public defaultLayoutService: DefaultLayoutService,
    private resolver: ComponentFactoryResolver) {
    }

    @ViewChild("appAsideContainer", { read: ViewContainerRef }) appAsideContainer: ViewContainerRef;

    ngOnInit() {

        //other component can call a method on the service, to control the layout...
        this.defaultLayoutService.e_openAppAside.subscribe(params => {

            let appAsideRef;

            //if there are no component inside it already, create one at least
            if (this.appAsideContainer.length == 0) {
                const appAsideFactory = 
                this.resolver.resolveComponentFactory(CustomAppAsideComponent);
                appAsideRef = this.appAsideContainer.createComponent(appAsideFactory);
            }

            let appAsideComponent = <CustomAppAsideComponent>appAsideRef.instance;

            //create comp. dynamically
            const factory = this.resolver.resolveComponentFactory(params.type);
            let component = appAsideComponent.container.createComponent(factory);

            //append all input values to components
            if (params.inputs) {
                for (let p in params.inputs) {
                    component.instance[p] = params.inputs[p];
            }


        });        
    }
 }

The issue is that members of appAsideComponent aren't accessible. It doesn't seem to be fully instantiated.

enter image description here

CustomAppAsideComponent in question is here

export class CustomAppAsideComponent {
  /** custom-app-aside ctor */
  constructor() {

  }

  @ViewChild("container", { read: ViewContainerRef }) public container: ViewContainerRef;
}

And its markup:

<app-aside [fixed]="true" [display]="false">
    <ng-template style="border: solid 2px;" #container></ng-template>
</app-aside>

app-aside is a component that generates a sidebar that opens vertically to the right.

I can usually create other components with no problem using this method, but it fails on this one. Both AppAsideComponent and CustomAppAsideComponent are in my module's entryComponent

Anything obvious I'm missing?

Upvotes: 2

Views: 1632

Answers (1)

Msk Satheesh
Msk Satheesh

Reputation: 1536

For this problem you have to create a ngAfterViewInit life cycle hook in the initially created dynamic component and if you dynamically create the first dynamic component you have the dynamic components native element on that you have to create second dynamic component.

<!-- app.component.html -->
<p>Page1</p>
<ng-container #sample1></ng-container>
/* app.component.ts */
import {  
    Component,  
    Input,  
    OnInit,  
    ViewChild,  
    ComponentFactoryResolver,  
    OnDestroy,  
    ViewContainerRef  
} from '@angular/core';  
import {  
    Page1Component  
} from './page1/page1.component';  

@Component({  
    selector: 'app-root',  
    templateUrl: './app.component.html',  
    styleUrls: ['./app.component.scss']  
})  
export class AppComponent {  
    title = 'dynamicloader';  
    @ViewChild('sample1', {  
        read: ViewContainerRef,
        static: false  
    }) sample: ViewContainerRef;  
    constructor(private componentFactoryResolver: ComponentFactoryResolver) {}  
    ngOnInit() {  
        this.sample.clear();  
        let page1ComponentFactory = 
 this.componentFactoryResolver.resolveComponentFactory(Page1Component);  
        let page1ComponentRef = this.sample.createComponent(page1ComponentFactory);    
    }  
} 
<!-- page1.component.html -->
<p>Page2</p>
<ng-container #sample2></ng-container>
/* page1.component.ts */
import {  
    Component,  
    Input,  
    OnInit,  
    ViewChild,  
    ComponentFactoryResolver,  
    OnDestroy,  
    ViewContainerRef,
    AfterViewInit
} from '@angular/core';  
import {  
    Page2Component  
} from './page2/page2.component';  

@Component({  
    selector: 'app-page1',  
    templateUrl: './page1.component.html',  
    styleUrls: ['./page1.component.scss']  
})  
export class Page1Component implements AfterViewInit {   
    @ViewChild('sample2', {  
        read: ViewContainerRef,
        static: false  
    }) sample2: ViewContainerRef;  
    constructor(private componentFactoryResolver: ComponentFactoryResolver) {}  
    ngAfterViewInit() {  
        this.sample2.clear();  
        let page2ComponentFactory = 
 this.componentFactoryResolver.resolveComponentFactory(Page2Component);  
        let page2ComponentRef = this.sample2.createComponent(page2ComponentFactory);
    }  
} 

Upvotes: 1

Related Questions