Render Syncfusion Angular components as Dynamic content in Dashboard Layout
This article explains how to render multiple components dynamically on the Angular Dashboard Layout component.
Get Started with Angular Dashboard Layout
Refer to this getting started to create an Angular Dashboard Layout component- https://ej2.syncfusion.com/angular/documentation/dashboard-layout/getting-started/
Render Dashboard Layout component
Render a dashboard Layout component using `ng-template`.
[app.component.html]
<div class="control-section"> <div style="padding:5px;text-align: right;"> <button id="add" class="e-btn e-info" (click)="addPanel()"> Add Panel </button> </div> <ejs-dashboardlayout id="default_dashboard" #default_dashboard columns="2" [allowResizing]="true" [cellSpacing]="cellSpacing" > <e-panels> <e-panel *ngFor="let panel of panelsData" [id]="panel.id" [sizeX]="panel.sizeX" [sizeY]="panel.sizeY" [row]="panel.row" [col]="panel.col" > <ng-template #content> <span style="float:right; margin-right:40px;padding-top:10px;font-weight: bold;" (click)="addWidget($event)" >ⓘ</span > <span id="close" class="e-template-icon e-close-icon" (click)="onCloseIconHandler($event)" ></span> <div class="e-panel-container"> <div class="text-align" [id]="panel.index"> <ng-template #template> </ng-template> </div></div ></ng-template> </e-panel> </e-panels> </ejs-dashboardlayout> <div id="template"></div> <div id="modalDialog"></div> <div id="dialogcontent"> <div> <div id="line_template"> <p class="dialog-text">Line Chart (1x1)</p> </div> <div id="bar_template"> <p class="dialog-text">Bar Chart (1x1)</p> </div> <div id="grid_template"> <p class="dialog-text">Grid (2x1)</p> </div> </div> </div> </div>
[app.component.ts]
 
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { ListBoxComponent } from '@syncfusion/ej2-angular-dropdowns';
import { DragAndDropEventArgs, NodeClickEventArgs, TreeViewComponent } from '@syncfusion/ej2-angular-navigations';
import { closest } from '@syncfusion/ej2-base';
import { AnalytesDragAndDrop } from './analytes-drag-and-drop';
import {
  Inject,
  ViewChildren,
  ViewContainerRef,
  QueryList,
  ComponentFactoryResolver,
  ComponentRef,
} from '@angular/core';
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { ListBoxComponent } from '@syncfusion/ej2-angular-dropdowns';
import { DragAndDropEventArgs, NodeClickEventArgs, TreeViewComponent } from '@syncfusion/ej2-angular-navigations';
import { closest } from '@syncfusion/ej2-base';
import { AnalytesDragAndDrop } from './analytes-drag-and-drop';
import {
  Inject,
  ViewChildren,
  ViewContainerRef,
  QueryList,
  ComponentFactoryResolver,
  ComponentRef,
} from '@angular/core';
import {
  DashboardLayoutComponent,
  PanelModel,
} from '@syncfusion/ej2-angular-layouts';
import { Dialog } from '@syncfusion/ej2-popups';
import { Chart, LineSeries, Category } from '@syncfusion/ej2-charts';
import { Button } from '@syncfusion/ej2-buttons';
import { ChartComponent } from './chart.component';
import { BarComponent } from './bar.component';
import { GridComponent } from './grid.component';
Chart.Inject(LineSeries, Category);
 
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  encapsulation: ViewEncapsulation.None,
})
 
export class AppComponent {
 
  @ViewChild('default_dashboard')
  public dashboard: DashboardLayoutComponent;
  componentRef: ComponentRef<ChartComponent>;
  componentRefOne: ComponentRef<BarComponent>;
  componentRefTwo: ComponentRef<GridComponent>;
  // @ViewChild('template', { read: ViewContainerRef })
  // viewTemplate: ViewContainerRef;
  @ViewChildren('template', { read: ViewContainerRef })
  viewTemplate: QueryList<ViewContainerRef>;
  constructor(private cfr: ComponentFactoryResolver) {}
  public count: number = 8;
  public dialogObj: Dialog;
  public target: any;
  public cellSpacing: number[] = [10, 10];
  public data: Object[] = [
    { month: 'Jan', sales: 35 },
    { month: 'Feb', sales: 28 },
    { month: 'Mar', sales: 34 },
    { month: 'Apr', sales: 32 },
    { month: 'May', sales: 40 },
    { month: 'Jun', sales: 32 },
    { month: 'Jul', sales: 35 },
    { month: 'Aug', sales: 55 },
    { month: 'Sep', sales: 38 },
    { month: 'Oct', sales: 30 },
    { month: 'Nov', sales: 25 },
    { month: 'Dec', sales: 32 }
  ];
  public axis: Object = {
    valueType: 'Category',
  };
 
  public panelsData: any = [
    {
      index: 0,
      id: 'one',
      sizeX: 1,
      sizeY: 1,
      row: 0,
      col: 0
    },
    {
      index: 1,
      id: 'two',
      sizeX: 3,
      sizeY: 1,
      row: 0,
      col: 1
    },
 
    {
      index: 2,
      id: 'three',
      sizeX: 2,
      sizeY: 1,
      row: 1,
      col: 0
    }
  ];
  addPanel(): void {
    let panel: PanelModel[] = [
      {
        id: this.count.toString(),
        sizeX: 1,
        sizeY: 1,
        row: 0,
        col: 0,
        content:
          '<span id="close" class="e-template-icon e-close-icon"></span><div class="text-align">' +
          this.count.toString() +
          '</div>'
      },
    ];
    this.dashboard.addPanel(panel[0]);
    let closeIcon: any = document
      .getElementById(this.count.toString())
      .querySelector('.e-close-icon');
    closeIcon.addEventListener('click', this.onCloseIconHandler.bind(this));
    this.count = this.count + 1;
  }
  onCloseIconHandler(event: any): void {
    if ((<HTMLElement>event.target).offsetParent) {
      this.dashboard.removePanel((<HTMLElement>event.target).offsetParent.id);
    }
  }
...
Render Angular Grid and Chart components
Render Data Grid, Line Chart, and Bar Chart components inside the app folder using Angular `template`.
[grid.component.ts]
import {
  Component,
  ViewEncapsulation,
  Input,
  Inject,
  ViewChild,
} from '@angular/core';
 
@Component({
  selector: 'grid-root',
  template: `<ejs-grid [dataSource]='gridData'>
  <e-columns>
      <e-column field='OrderID' headerText='Order ID' textAlign='Right' width=90></e-column>
      <e-column field='CustomerID' headerText='Customer ID' width=120></e-column>
      <e-column field='Freight' headerText='Freight' textAlign='Right' format='C2' width=90></e-column>
      <e-column field='OrderDate' headerText='Order Date' textAlign='Right' format='yMd' width=120></e-column>
  </e-columns>
  </ejs-grid>`,
  encapsulation: ViewEncapsulation.None,
})
export class GridComponent {
  @Input() gridData: Object[];
  ngOnInit(): void {}
}
 
 [chart.component.ts]
import {
  Component,
  ViewEncapsulation,
  Input,
  Inject,
  ViewChild,
} from '@angular/core';
 
@Component({
  selector: 'chart-root',
  template: `<ejs-chart
  [primaryXAxis]="primaryXAxis"
  width="100%"
  height="100%"
>
  <e-series-collection>
    <e-series
      [dataSource]="chartData"
      type="Line"
      xName="month"
      yName="sales"
      name="Sales"
    ></e-series>
  </e-series-collection>
</ejs-chart>`,
  encapsulation: ViewEncapsulation.None,
})
export class ChartComponent {
  @Input() primaryXAxis: Object;
  @Input() chartData: Object[];
  ngOnInit(): void {
    // Data for chart series
  }
}
 
 [bar.component.ts]
import {
  Component,
  ViewEncapsulation,
  Input,
  Inject,
  ViewChild,
} from '@angular/core';
 
@Component({
  selector: 'bar-root',
  template: `<ejs-chart
  style="display:block;"
  [chartArea]="chartArea"
  width="100%"
  height="100%"
  [primaryXAxis]="primaryXAxis"
  [primaryYAxis]="primaryYAxis"
  [title]="title"
  [tooltip]="tooltip"
>
  <e-series-collection>
    <e-series
      [dataSource]="data"
      type="Bar"
      xName="x"
      yName="y"
      name="Imports"
      [marker]="marker"
    >
    </e-series>
    <e-series
      [dataSource]="data1"
      type="Bar"
      xName="x"
      yName="y"
      name="Exports"
      [marker]="marker"
    >
    </e-series>
  </e-series-collection>
</ejs-chart>`,
  encapsulation: ViewEncapsulation.None,
})
export class BarComponent {
  public chartArea: Object = {
    border: {
      width: 0,
    }
  };
  //Initializing Chart Width
  @Input() data: Object[];
  @Input() data1: Object[];
  //Initializing Marker
  public marker: Object = {
    dataLabel: {
      visible: true,
      position: 'Top',
      font: {
        fontWeight: '600',
        color: '#ffffff',
      },
    },
  };
  //Initializing Primary X Axis
  public primaryXAxis: Object = {
    valueType: 'Category',
    title: 'Food',
    interval: 1,
    majorGridLines: { width: 0 },
  };
  //Initializing Primary Y Axis
  public primaryYAxis: Object = {
    labelFormat: '{value}B',
    edgeLabelPlacement: 'Shift',
    majorGridLines: { width: 0 },
    majorTickLines: { width: 0 },
    lineStyle: { width: 0 },
    labelStyle: {
      color: 'transparent',
    },
  };
  public tooltip: Object = {
    enable: true,
  };
 
  // custom code end
  public title: string = 'UK Trade in Food Groups - 2015';
  constructor() {
    //code
  }
}
 
Render dynamic components as content of the Dashboard Layout
Render Angular component as panel content of the Dashboard Layout based on the selection using Angular’s `ComponentFactoryResolver’.
[app.component.ts]
export class AppComponent {
 
  ...
  addWidget(event) {
    this.target = event.target.closest('.e-panel').querySelector('.text-align');
    if (this.dialogObj == undefined) {
      this.dialogObj = new Dialog({
        width: '500px',
        header: 'Add a widget',
        showCloseIcon: true,
        animationSettings: { effect: 'Zoom' },
        content: document.getElementById('dialogcontent'),
        target: document.getElementById('target'),
        isModal: true,
        height: '260px',
        visible: false,
      });
      this.dialogObj.appendTo('#modalDialog');
    }
    this.dialogObj.show();
    document.getElementById('line_template').onclick = () => {
      this.dialogObj.hide();
      //Get the panel index.
      var count = this.target.id;
      this.viewTemplate.map((vcr: ViewContainerRef, index: number) => {
        //Check whether the panel index matches reference index.
        if (index == count && this.target.innerText == '') {
          const componentFactory =
            this.cfr.resolveComponentFactory(ChartComponent);
          this.componentRef = vcr.createComponent(componentFactory);
          this.componentRef.instance.chartData = this.data;
          this.componentRef.instance.primaryXAxis = this.axis;
        }
      });
    };
    document.getElementById('bar_template').onclick = () => {
      this.dialogObj.hide();
      var count = this.target.id;
      this.viewTemplate.map((vcr: ViewContainerRef, index: number) => {
        if (index == count && this.target.innerText == '') {
          const componentFactory =
            this.cfr.resolveComponentFactory(BarComponent);
          this.componentRefOne = vcr.createComponent(componentFactory);
          this.componentRefOne.instance.data = [
            { x: 'Egg', y: 2.2 },
            { x: 'Fish', y: 2.4 },
            { x: 'Misc', y: 3 },
            { x: 'Tea', y: 3.1 },
          ];
          this.componentRefOne.instance.data1 = [
            { x: 'Egg', y: 1.2 },
            { x: 'Fish', y: 1.3 },
            { x: 'Misc', y: 1.5 },
            { x: 'Tea', y: 2.2 },
          ];
        }
      });
    };
    document.getElementById('grid_template').onclick = () => {
      this.dialogObj.hide();
      var count = this.target.id;
      this.viewTemplate.map((vcr: ViewContainerRef, index: number) => {
        if (index == count && this.target.innerText == '') {
          const componentFactory =
            this.cfr.resolveComponentFactory(GridComponent);
          this.componentRefTwo = vcr.createComponent(componentFactory);
          this.componentRefTwo.instance.gridData = [
            {
              OrderID: 10248,
              CustomerID: 'VINET',
              EmployeeID: 5,
              OrderDate: new Date(8364186e5),
              ShipName: 'Vins et alcools Chevalier',
              ShipCity: 'Reims',
              ShipAddress: '59 rue de l Abbaye',
              ShipRegion: 'CJ',
              ShipPostalCode: '51100',
              ShipCountry: 'France',
              Freight: 32.38,
              Verified: !0,
            },
            {
              OrderID: 10249,
              CustomerID: 'TOMSP',
              EmployeeID: 6,
              OrderDate: new Date(836505e6),
              ShipName: 'Toms Spezialitäten',
              ShipCity: 'Münster',
              ShipAddress: 'Luisenstr. 48',
              ShipRegion: 'CJ',
              ShipPostalCode: '44087',
              ShipCountry: 'Germany',
              Freight: 11.61,
              Verified: !1,
            },
            {
              OrderID: 10250,
              CustomerID: 'HANAR',
              EmployeeID: 4,
              OrderDate: new Date(8367642e5),
              ShipName: 'Hanari Carnes',
              ShipCity: 'Rio de Janeiro',
              ShipAddress: 'Rua do Paço, 67',
              ShipRegion: 'RJ',
              ShipPostalCode: '05454-876',
              ShipCountry: 'Brazil',
              Freight: 65.83,
              Verified: !0,
            },
          ];
        }
      });
    };
  }
 
}
 
 Screenshot:

GitHub sample - https://github.com/SyncfusionExamples/angular-dashboard-layout-dynamic-components
Conclusion
We hope you enjoyed learning about how to render Syncfusion Angular components as Dynamic content in Dashboard Layout.
You can refer to our Angular feature tour page to learn about its other groundbreaking feature representations and documentation, and how to quickly get started with configuration specifications. You can also explore our Angular dashboard-layout example to understand how to create and manipulate data.
For current customers, you can check out our components from the License and Downloads page. If you are new to Syncfusion, you can try our 30-day free trial to explore our other controls.
If you have any queries or require clarifications, please let us know in the comments section below. You can also contact us through our support forums, BoldDesk Support, or feedback portal. We are always happy to assist you!