Dynamic Components in Angular with TypeScript – DEVELOPPARADISE
17/07/2018

Dynamic Components in Angular with TypeScript

Introduction

Today, we will be going through the step by step discussion of how to create dynamic components in Angular 2/4 and how to set the value of dynamic control and get the value from the dynamically generated component. Dynamic Components are those components which generate at runtime. Runtime in the sense, we have a running application and if we want to render some component on any event like button click event, then it will be called the Dynamic Component. So, this article will cover four things about Dynamic Components as follows:

  1. How to create Dynamic Component
  2. How to destroy Dynamic Component
  3. How to set the value of controls in Dynamic Component
  4. How to get the value from Dynamic Component controls

Background

In this demonstration, we will create a similar screen as shown below. Here you can see that we have two portions of the screen, in the first portion, we have one “Add New Skills” button and clicking on this, it will add a new dynamic component which has few controls like dropdown for Skills, a textbox for entering the Experiences value and dropdown for Rating. Apart from this, at the end, we have a cross button which will delete the dynamic component. Once data will be prepared using controls in the first part and when we click to Save Skills button, then we will get the values from controls and show it in the second portion.

So, in this screen, you will see first how to create an Angular dynamic component on clicking “Add New Skills” button, how to destroy the dynamic component on clicking on “Cross” button and apart from this, we will also see how to set and get the value for dynamic components in Angular.

Dynamic Components in Angular with TypeScript

Using the Code

So, let’s create an Angular 5 application using CLI in Visual Studio Code. If you are a beginner and don’t know how to create an Angular CLI project, then you can follow this article where you will learn:

We are moving to the next step because we hope you are able to create an Angular 5 CLI project either in Visual Studio 2015/2017 or Visual Studio Code. So, let’s move to a practical demonstration for creating a Dynamic Component. After creating the project, open “Index.html” and add the CDN path of bootstrap so that we can use bootstrap CSS. As you can see, we have added bootstrap.min.css inside the <head> tag.

<!doctype html>
<html lang="en"><head>
    <meta charset="utf-8">
    <title>DynamicComponentDemo</title>
    <base href="/">    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" 

     rel="stylesheet" id="bootstrap-css">
</head><body>
    <app-root></app-root>
</body></html>

Now let’s create one new component which will have three controls as drop-down for Skills, a textbox for Experiences and dropdown for Rating. So, first create a folder name as “components” inside the app directory and generate a component using the following command:

ng g c SkillsRating --save

Once you will able to generate a new component as “SkillsRatingComponent“, just go and add this component “SkillsRatingComponent” inside declarations section of the “AppModule“. Apart from that, we have to import “FormsModule” or “ReactiveFormsModule” for using the form’s controls. If you don’t do it, you will get a few errors for forms controls.

AppModule.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';
import { SkillsRatingComponent } from './components/skills-rating/skills-rating.component';

@NgModule({
  declarations: [
    AppComponent,
    SkillsRatingComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents:[SkillsRatingComponent]
})
export class AppModule { }

Now, it’s time to modify SkillsRatingComponent. So, open the “skills-rating-component.html” and add the following template code. You can see with the following code, we have on the dropdown for showing the list of user skills, we have on textbox for user experience, we have one more dropdown for showing the rating provided by the user and at the last, we have one button for removing the skills data, means removing the dynamic component.

skills-rating-component.html

<div class="form-inline" style="margin: 10px;" id="row{{index}}">
    <b>Skills:</b>
    <select [(ngModel)]="selectedSkill" class="form-control">
        <option *ngFor="let item of skills" [value]="item">{{item}}</option>
    </select>

    <b>Experiences: </b>
    <input [(ngModel)]="yearsOfExperiences" type="text" class="form-control" 

     style="margin-left:10px; margin-right:10px; width: 60px;">

    <b>Rating: </b>
    <select [(ngModel)]="selectedRating" class="form-control" style="width:70px;">
        <option *ngFor="let item of ratings" [value]="item">{{item}}</option>
    </select>

    <img src="../../../../../assets/remove.png" width="15" height="15" 

     title="Remove" (click)="removeSkills()" />
</div>

After decorating the template for SkillsRatingComponent, now it’s time to modify its component. So, open SkillsRatingComponent.ts and add the following codes. Here, you can see that we are declaring so many properties. So, let me explain it one by one, reference properties will take care of the reference of the dynamically generated components, an index is the index number of how many dynamic components are generated, selectedSkill, yearsOfExperiences and selectedRating are the selected value for skills, experience and rating respectively. Apart from that, we have two more properties as skills and ratings, these will use to bind the data in the dropdown.

Here, you can note that we have one method called removeSkills() which will destroy the selected dynamic button on clicking on the Cross button.

SkillsRatingComponent.ts

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

@Component({
  selector: 'app-skills-rating',
  templateUrl: './skills-rating.component.html',
  styleUrls: ['./skills-rating.component.css']
})
export class SkillsRatingComponent implements OnInit {

  reference: any;
  index: number;

  selectedSkill: string;
  yearsOfExperiences: string = '0';
  selectedRating: string;

  skills: any = [];
  ratings: any = [];

  constructor(private elementRef: ElementRef) {
  }

  removeSkills() {
    this.reference.destroy();
  }

  ngOnInit() {
  }
}

So, now we have ready that component and its template which will generate dynamically by clicking on the button “Add New Skills“. So, let’s move back to app.component.html and the following code inside it. Here you can see that we have two portions as we already discussed above, the first one has one button for adding new skills along with one #dynamicContainer. This dynamic container is a container for adding all dynamically generated components inside it on click of “add new skills”.

The second portion is containing only one span tag with id as “addedSkills” which will show all the list of selected skills from the first portions on clicking on Save Skills button.

app.component.html

<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h2 style="text-align: center;">Dynamic Components in Angular</h2>
        </div>
        <div class="col-md-7" style="border: 1px solid gray; height: 500px; padding: 10px;">
            <div style="float:left; margin-bottom:5px;" id="dynamicContainer">
                <button type="button" (click)="addNewSkills()" width="20" height="20" 

                 class="btn btn-primary" style=" margin-left:10px;" 

                 title="Add New Skills">Add New Skills</button>

                <!--Create container for Dynamic Components -->
                <div #dynamicContainer></div>

                <button type="button" (click)="saveSkills()" 

                 *ngIf="embeddedViews>0" width="20" height="20" class="btn btn-primary" 

                 style=" margin-left:10px;" title="Save Skills">Save Skills</button>
            </div>
        </div>
        <div class="col-md-5" style="border: 1px solid gray; height: 500px;">
            <h3>Skills Rating By User</h3>
            <span id="addedSkills"></span>
        </div>
    </div>
</div>
<router-outlet></router-outlet>

Finally, we have to move AppComponent.ts where we will prepare the data to bind with dynamically generated controls.

So, first of all, get the instance of dynamicContainer using ViewContainerRef and apart from this, create the dependency injection for ComponentFactoryResolver. ComponentFactoryResolver is used to resolve component which is passed on runtime. Once component will resolve, the dynamic component can be created inside the container using the CreateComponent method which takes the resolved component as arguments.

let comp = this.comFacResolver.resolveComponentFactory(SkillsRatingComponent);
let dynamicComp = this.container.createComponent(comp);

Once the dynamic component is generated, it’s time to bind its controls on runtime. First of all, set the reference with dynamicComp object. After that, you can see with the following code, we have set up each property and bind with its values.

dynamicComp.instance.reference = dynamicComp;
dynamicComp.instance.skills = this.skills;
dynamicComp.instance.ratings = this.ratings;
dynamicComp.instance.index = this.rowId;
dynamicComp.instance.selectedSkill = '';
dynamicComp.instance.yearsOfExperiences = '0';
dynamicComp.instance.selectedRating = this.ratings[0];

Till above, we have seen how to create the dynamic component, destroy the dynamic component and set the value for the dynamic component. But the question is how to get the value of control from dynamically generated components. When clicking on the Save Skills button, first we have to create the instance of the container and then inside that, we can find all the embedded view which is nothing but individual the dynamically generated component. So, using for loop, we can get the value from each embedded view and append it to the second portion’s span.

You can find the whole code for AppComponent as follows:

AppComponent.ts

import { Component, ViewContainerRef, ViewChild, ComponentFactoryResolver } from '@angular/core';
import { SkillsRatingComponent } from './components/skills-rating/skills-rating.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  @ViewChild('dynamicContainer', { read: ViewContainerRef }) container: ViewContainerRef;
  skills: any;
  ratings: any;
  rowId: number;
  data: string;
  embeddedViews: number = 0;

  constructor(private comFacResolver: ComponentFactoryResolver) {
  }

  addNewSkills() {
    let rows = document.getElementById("dynamicContainer");
    let rowIdIndex = rows.innerHTML.indexOf("row");
    if (rowIdIndex == -1) {
      this.rowId = 1;
    }

    this.skills = ['CSharp', '.Net Framework', 'Asp.Net', 
    'Asp.Net Core', 'Angular 1.x', 'Angular 2.x', 'Web API', 'Azure', 'Javascript', 'SQL'];
    this.ratings = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    let comp = this.comFacResolver.resolveComponentFactory(SkillsRatingComponent);
    let dynamicComp = this.container.createComponent(comp);
    dynamicComp.instance.reference = dynamicComp;

    dynamicComp.instance.skills = this.skills;
    dynamicComp.instance.ratings = this.ratings;
    dynamicComp.instance.index = this.rowId;

    dynamicComp.instance.selectedSkill = '';
    dynamicComp.instance.yearsOfExperiences = '0';
    dynamicComp.instance.selectedRating = this.ratings[0];

    this.rowId += 1;

    let com = this.container;
    if (com !== undefined) {
      this.embeddedViews = com['_embeddedViews'].length;
    }
  }

  saveSkills() {
    let comp = this.container;
    this.data = "";
    (<HTMLSpanElement>document.getElementById("addedSkills")).innerText = "";

    if (comp !== undefined) {
      debugger;
      if (comp['_embeddedViews'].length != 0) {
        for (let i = 0; i < comp['_embeddedViews'].length; i++) {
          if (comp['_embeddedViews'][i] != undefined) {
            debugger;
            let selectedSkill = comp['_embeddedViews'][i].nodes[1].instance.selectedSkill == 
                         '' ? "NONE" : comp['_embeddedViews'][i].nodes[1].instance.selectedSkill;
            let yearsOfExperiences = comp['_embeddedViews'][i].nodes[1].
                 instance.yearsOfExperiences == '' ? "NONE" : 
                 comp['_embeddedViews'][i].nodes[1].instance.yearsOfExperiences;
            let selectedRating = comp['_embeddedViews'][i].nodes[1].instance.selectedRating;

            this.data = 'User knows <b style="color:green;">' + selectedSkill + 
            ' </b> from <b>' + yearsOfExperiences + ' years</b> , provided rating as <b>' + 
            selectedRating + '</b>.';

            (<HTMLSpanElement>document.getElementById("addedSkills")).innerHTML += this.data;
            (<HTMLSpanElement>document.getElementById("addedSkills")).
                            appendChild(document.createElement('br'));
          }
        }
      }
    }
  }
}

Once you set up each and everything as suggested above and run the project, then you will find the same screen which we have already shown above. From there, we can create and destroy the dynamic component and get or set the value of dynamic component.

Conclusion

So, today, we have learned how to create the dynamic component in Angular, how to destroy the dynamic component in Angular and how to set and get the value for dynamically generated components.

I hope this post will help you. Please put your feedback using comment which helps me to improve myself for the next post. If you have any doubts, please ask your doubts or query in the comment section and if you like this post, please share it with your friends. Thanks!