Angular - Using ViewContainerRef



The second way of creating and rendering components dynamically is by using ViewContainerRef. Other components or directives use it to get a reference to a view container corresponding to their location in the DOM. In this way, ViewContainerRef helps to dynamically create and insert components at that specific location.

Example - Use of ViewContainerRef

Let us create a real time application to display a collection of employees in two different formats, table and gallery. We will create two components, one to show employees in the table and another in gallery format. Then we will create a host component, which will dynamically load either table based component or gallery based component.

Step 1: Create an interface to hold common data across multiple dynamic component

$ ng generate interface DynData
CREATE src/app/dyn-data.ts (29 bytes)

Step 2: Create a dynamic component, DynList. The purpose of the component is to list the employee as gallery.

$ ng generate component DynList
CREATE src/app/dyn-list/dyn-list.css (0 bytes)
CREATE src/app/dyn-list/dyn-list.html (23 bytes)
CREATE src/app/dyn-list/dyn-list.spec.ts (567 bytes)
CREATE src/app/dyn-list/dyn-list.ts (209 bytes)

Step 3: Create a dynamic component, DynTable. The purpose of the component is to list the employees in tabular format.

$ ng generate component DynTable
CREATE src/app/dyn-table/dyn-table.css (0 bytes)
CREATE src/app/dyn-table/dyn-table.html (24 bytes)
CREATE src/app/dyn-table/dyn-table.spec.ts (574 bytes)
CREATE src/app/dyn-table/dyn-table.ts (213 bytes)

Step 4: Create a host component, DynHost. The purpose of the component is to host any one of the component, DynList and DynTable based on configuration.

$ ng generate component DynHost
CREATE src/app/dyn-host/dyn-host.css (0 bytes)
CREATE src/app/dyn-host/dyn-host.html (23 bytes)
CREATE src/app/dyn-host/dyn-host.spec.ts (567 bytes)
CREATE src/app/dyn-host/dyn-host.ts (209 bytes)

Step 5: Next, open the interface, DynData and add an array property, data

DynData.ts

export interface DynData {
   data : any[]
}

Step 6: Next, open the DynList component and implement DynData interface.

DynList.ts

import { Component } from '@angular/core';
import { DynData } from '../dyn-data';

@Component({
   selector: 'app-dyn-list',
   imports: [],
   templateUrl: './dyn-list.html',
   styleUrl: './dyn-list.css',
})
export class DynList implements DynData {
   data: any[] = []
}

Here,

  • Imported DynData interface and implements it in class definition

  • Included the data property as per DynData interface specification

Step 7: Next, open the components template and render the data as list of items

dyn-list.html

<div class="gallery">
    @for (item of data; track $index) {
   <div class="card">
      <div class="container">
         <h4><b>{{ item.name }}</b></h4>
         <p>{{ item.role }}</p>
      </div>
   </div>
   }
</div>

Here,

  • data holds the list of employees with two properties, name and role

  • Used @for to show the employees as list of cards

Step 8: Next, open the components styles and add necessary CSS as shown below −

dyn-list.css

.gallery {
   display: flex;
   flex-wrap: wrap;
   justify-content: left;
   gap: 10px;
}

.card {
   flex-basis: 200px;
   box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
}

.card:hover {
   box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}

.container {
   padding: 2px 16px;
   max-width: 200px;
}

Here, we used flex feature of CSS to show the employees in gallery format

Step 9: Next, open the DynTable component and implement DynData interface.

DynTable.ts

import { Component } from '@angular/core';
import { DynData } from '../dyn-data';

@Component({
   selector: 'app-dyn-table',
   imports: [],
   templateUrl: './dyn-table.html',
   styleUrl: './dyn-table.css',
})
export class DynTable implements DynData {
   data: any[] = []
}

Here,

  • Imported DynData interface and implements it in class definition

  • Included the data property as per DynData interface specification

Step 10: Next, open the components template and render the data as list of items

Dyn-Table.html

<table class="employees">
   <thead>
      <tr>
         <th>Name</th>
         <th>Role</th>
      </tr>
   </thead>
   <tbody>
    @for (item of data; track $index) {
      <tr>
         <td>{{ item.name }}</td>
         <td>{{ item.role }}</td>
      </tr>
    }
   </tbody>
</table>

Here,

  • data hold the list of employees with two properties, name and role

  • Used ngFor to render the employees as rows in the html table

Step 11: Next, open the components styles and add necessary CSS as shown below −

.employees {
   border-collapse: collapse;
   width: 400px;
}

.employees td, .employees th {
   padding: 8px;
}

.employees tbody tr:nth-child(even){background-color: #f2f2f2;}

.employees tbody tr:hover {background-color: #ddd;}

.employees thead th {
   padding-top: 12px;
   padding-bottom: 12px;
   text-align: left;
   background-color: brown;
   color: white;
}

Step 12: Next, open DynHost components template and include the ng-container as shown below −

<ng-container #container></ng-container>

Here, we have shown the usage of ng-container.

Step 13: Next, open DynHostComponent and import necessary classes

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

Step 14: Next, import interface, list component, table component and directive

import { DynData } from '../dyn-data';
import { DynList } from '../dyn-list/dyn-list';
import { DynTable } from '../dyn-table/dyn-table';

Step 15: Implement AfterViewInit life cycle hook in the class declaration

export class DynHost implements AfterViewInit {
}

Step 16: Declare an input property to get format information (table / list) from user.

@Input() format: string = 'list'

Step 17: Declare a property, data and set the sample data

private data = [
   {
      'name': 'John',
      'role': "Manager"
   },
   {
      'name': 'Peter',
      'role': "Marketing Intern"
   },
   {
      'name': 'Mary',
      'role': "Technical Intern"
   },
   {
      'name': 'Jack',
      'role': "Sales Manager"
   },
   {
      'name': 'Jessica',
      'role': "Delivery Head"
   },
]

Step 18: Get the ng-container view component from the template by using @ViewChild

@ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;

Step 19: Do actual implementation of dynamically creating the component and loading it into the appropriate place in the ngOnInit life cycle hook

ngAfterViewInit() {
      
   if(this.format == 'table') {
      const compRef = this.container.createComponent<DynData>(DynTable);
      compRef.instance.data = this.data;
   } else {
      const compRef = this.container.createComponent<DynData>(DynList);
      compRef.instance.data = this.data;
   }
}

Step 20: The complete listing of the host component is as follows,

import { AfterViewInit, Component, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';

import { DynData } from '../dyn-data';
import { DynList } from '../dyn-list/dyn-list';
import { DynTable } from '../dyn-table/dyn-table';

@Component({
   selector: 'app-dyn-host',
   templateUrl: './dyn-host.html',
   styleUrls: ['./dyn-host.css']
})
export class DynHost implements AfterViewInit {
   @Input() format: string = 'table'
   @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
   
   private data = [
   {
      'name': 'John',
      'role': "Manager"
   },
   {
      'name': 'Peter',
      'role': "Marketing Intern"
   },
   {
      'name': 'Mary',
      'role': "Technical Intern"
   },
   {
      'name': 'Jack',
      'role': "Sales Manager"
   },
   {
      'name': 'Jessica',
      'role': "Delivery Head"
   },
  ]
   
   ngAfterViewInit() {
      
      if(this.format == 'table') {
         const compRef = this.container.createComponent<DynData>(DynTable);
         compRef.instance.data = this.data;
      } else {
         const compRef = this.container.createComponent<DynData>(DynList);
         compRef.instance.data = this.data;
      }
   }
}

Step 21: Next, open the app components template and include the host component.

app.html

<app-dyn-host format="table" />

Here, we have directed the host component to render the employees data in the tabular format. The host component will dynamically create the DynTableComponent and inject into the host component template.

Step 22: Next, run the application and you can see that the employee data is shown in a tabular format as shown below −

employee data

Step 23: Next, open the app components template and change the format to gallery.

<app-dyn-host format="gallery" />

Here, we have directed the host component to render the employees data in gallery format. The host component will dynamically create the DynListComponent and inject into the host component template.

Output

Step 24: Next, run the application and you can see that the employee data is shown in gallery format as shown below −

gallery format
Advertisements