How to place a dynamic component in a container

TypescriptAngular

Typescript Problem Overview


I want to create dynamic components and insert views of these components to a container.

I think this can be achieved by ViewContainerRef.

But I don't know, can we get ViewContainerRef of any component? if yes then how?. I am new to Angular, if there are any other good solutions available to handle this scenario, please suggest me.

Updated I think, I am pretty much near to the solution. Below is the code.

app.component.ts

import {Component} from '@angular/core';
import {ContainerComponet} from './container.component'

@Component({
    selector: 'my-app',
    template: `
    <container> </container>
    `,
    directives: [ContainerComponet]
})
export class AppComponent {

    constructor() { }
    
 }

container.component.ts

import {Component, ComponentResolver, ViewContainerRef} from '@angular/core'
import {controlBoxComponent as controlBox} from './controlBox.component';

@Component({
    selector: 'container',
    template: 'container'    
})
export class ContainerComponet {
    constructor(viewContainer: ViewContainerRef, private _cr: ComponentResolver) {
        
        this._cr.resolveComponent(controlBox)
            .then(cmpFactory => {
                const ctxInjector = viewContainer.injector;
                return viewContainer.createComponent(cmpFactory, 0,  ctxInjector);
            })

    }
}

controlBox.component.ts

import {Component} from '@angular/core'
@Component({
    selector: 'controlBox',
    template: 'controlBox'
})
export class controlBoxComponent {
    constructor() { }
}

Output

<my-app>
    <container>container</container><controlbox _ngcontent-lsn-3="">controlBox</controlbox>
</my-app>

Expected Result

<my-app>
    <container>container
	<controlbox _ngcontent-lsn-3="">controlBox</controlbox>
	</container>
</my-app>

Typescript Solutions


Solution 1 - Typescript

You can get the ViewContainerRef of the current component by or from an element in the view of the current component

@Component({
  selector: '...',
  directives: [OtherComponent, FooComponent],
  template: `
    <other-component></other-component>
    <foo-component #foo></foo-component>
    <div #div></div>`
})

export class SomeComponent {
  // `ViewContainerRef` from an element in the view
  @ViewChild(OtherComponent, {read: ViewContainerRef}) other;
  @ViewChild('foo', {read: ViewContainerRef}) foo;
  @ViewChild('div', {read: ViewContainerRef}) div;

  // `ViewContainerRef` from the component itself
  constructor(private viewContainerRef:ViewContainerRef, private componentFactoryResolver: ComponentFactoryResolver) {}

  let factory = this.componentFactoryResolver.resolveComponentFactory(ControlBox)
  this.componentRef = this.other.createComponent(factory);
  // this.componentRef = this.foo.createComponent(factory);
  // this.componentRef = this.div.createComponent(factory);
  // this.componentRef = this.viewContainerRef.createComponent(factory);
  });
}

See also https://stackoverflow.com/questions/36325212/angular-2-dynamic-tabs-with-user-click-chosen-components/36325468#36325468

Solution 2 - Typescript

I did something like that for my app. To load datas in a table.

To do that I've creat a directive :

directives: [TableDirective]

And then I use it like that :

@ViewChild(TableDirective) tableDirective:TableDirective;

ngAfterViewInit() {
    setTimeout(_=>this.load());
}

load() {
    this.tableDirective.loadTable(*ADirectiveToLoad*);
}

The TableDirective File :

import { Component, DynamicComponentLoader, ViewContainerRef } from 'angular2/core';

@Component({
    selector: "my-table",
    template: `<my-data></my-data>`
})

export class TableDirective {
    constructor(
        private dcl:DynamicComponentLoader,
        private viewContainerRef:ViewContainerRef) {
    }

public loadTable(base:any) {
    this.viewContainerRef.clear();
    this.dcl.loadNextToLocation(base, this.viewContainerRef);
}
}

This will load datas in my table, depend of the Directive I send. For exemple :

import { Component, OnInit } from 'angular2/core';

@Component({
    selector: "my-data",
    templateUrl: "app/_includes/table/actionnaire/table.html"
})

export class ActionnaireDirective implements OnInit {
    private entity:any;

ngOnInit() {
    this.entity = ACTIONNAIRES_PORTEUR;
}
}

var ACTIONNAIRES_PORTEUR:Actionnaire[] = [
    {"id": 1, "nom": "Test", "prenom": "Testeur", "dateNaissance": "15/05/1995"}
];

export class Actionnaire {
    id:number;
    nom:string;
    prenom:string;
    dateNaissance:any;
}

I'm also new with Angular :x

Solution 3 - Typescript

I was searching for a solution to this problem as well.

The only way I was able to make it happen was to use an additional Component

import {Component, ViewContainerRef} from '@angular/core';

@Component({
    selector: 'sw-view-container-ref',
    template: `<div></div>`
})

export class SwViewContainerRef {

    private _viewContainerRef:ViewContainerRef;

    constructor(viewContainerRef:ViewContainerRef) {
        this._viewContainerRef = viewContainerRef;
    }

    get viewContainerRef():ViewContainerRef {
        return this._viewContainerRef;
    }
}

container.component.ts

import {Component, ComponentResolver, ViewContainerRef, AfterViewInit, ViewChild,Injector} from '@angular/core'
import {controlBoxComponent as controlBox} from './controlBox.component';
import {SwViewContainerRef} from "./sw-view-container-ref";

@Component({
    selector: 'container',
    template: 'container<sw-view-container-ref #swViewContainerRef></sw-view-container-ref>',
    directives: [SwViewContainerRef]
})
export class ContainerComponet implements AfterViewInit {

    @ViewChild('swViewContainerRef', SwViewContainerRef) swViewChild:SwViewContainerRef;

    ngAfterViewInit() {
        this.desiredViewContainerRef = this.swViewChild.viewContainerRef;

        var self = this;

        this._cr.resolveComponent(controlBox).then((factory) => {

            var componentRef = self.desiredViewContainerRef.createComponent(factory, null, self.injector, null);
            componentRef.changeDetectorRef.detectChanges();
            componentRef.onDestroy(()=> {
                componentRef.changeDetectorRef.detach();
            })

            return componentRef;
        });
    }

    public desiredViewContainerRef:ViewContainerRef;

    constructor(private _cr: ComponentResolver, public injector:Injector) {

    }
}

It should produce something similar to this.

<my-app>
<container>container
<sw-view-container-ref><div></div></sw-view-container-ref>
<controlbox>controlBox</controlbox>
</container>
</my-app>

I'm not sure if my examples are clear or working, feel free to ask questions or make suggestions, I will try to answer and update my example.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPrem PariharView Question on Stackoverflow
Solution 1 - TypescriptGünter ZöchbauerView Answer on Stackoverflow
Solution 2 - TypescriptAastalView Answer on Stackoverflow
Solution 3 - TypescriptSebastianView Answer on Stackoverflow