Overview Of Dynamic Components In Angular

Angular

Components are the basic building blocks of any Angular project. In this article we will cover some advanced topics about the components, such as how to create dynamic components and also how to create some advanced components in Angular projects.  First of all we will learn how to create dynamic components in Angular projects.

 Let’s assume that I have two component, “firstComponent” and “secondComponent”.  Following is the content of both components.

first.component.ts

  1. import { Component, OnInit } from '@angular/core';  
  2.   
  3. @Component({  
  4.   selector: 'app-first',  
  5.   templateUrl: './first.component.html',  
  6.   styleUrls: ['./first.component.css']  
  7. })  
  8. export class FirstComponent implements OnInit {  
  9.   
  10.   message:string='';  
  11.   constructor() { }  
  12.   
  13.   ngOnInit() {  
  14.   }  
  15.   

first.component.html

  1. <p>  
  2.    {{message}}  
  3. </p> 

second.component.ts

  1. import { Component, OnInit } from '@angular/core';  
  2.   
  3. @Component({  
  4.   selector: 'app-second',  
  5.   templateUrl: './second.component.html',  
  6.   styleUrls: ['./second.component.css']  
  7. })  
  8. export class SecondComponent implements OnInit {  
  9.   
  10.   constructor() { }  
  11.   message:string='';  
  12.   ngOnInit() {  
  13.   }  
  14.   
  15. }  

second.component.html

  1. <p>  
  2. {{message}}  
  3. </p>  

 I also have another component, “app”  component, in which I will load the above components at dynamic times.

App.component.ts

  1. import { Component } from '@angular/core';  
  2.   
  3. @Component({  
  4.   selector: 'app-root',  
  5.   templateUrl: './app.component.html',  
  6.   styleUrls: ['./app.component.css']  
  7. })  
  8. export class AppComponent {  
  9.   title = 'Advanced Angular App';  

App.component.html

  1. <!--The content below is only a placeholder and can be replaced.-->  
  2. <div style="text-align:center">  
  3.   <h1>  
  4.     Welcome to {{title}}!  
  5.   </h1>  
  6.   
  7.   <button type="button" >First Component</button>  
  8.   <button type="button" >Second Component</button>  
  9. </div>  

This “App” component is a Bootstrap component of my application, so when I run the application the following screen will be visible in the browser.

Angular
Now my task is that I want to make one component visible at a time, that means if I click on “First Component” button then “first” component should be visible in my app component and if I click on “Second Component” button then “second” component should be visible in app component. 

So what will be my approach to achieve this? If we ask ourselves this question, I think the first solution that comes in our minds will be to show both components on any condition basis; that means use the show/hide mechanism to achieve the target like below.

  1. <!--The content below is only a placeholder and can be replaced.-->  
  2. <div style="text-align:center">  
  3.   <h1>  
  4.     Welcome to {{title}}!  
  5.   </h1>  
  6.   
  7.   <button type="button" >First Component</button>  
  8.   <button type="button" >Second Component</button>  
  9. <div>  
  10.   <app-first *ngIf="componentVisible=='first'"></app-first>  
  11.   <app-second *ngIf="componentVisible=='second'"></app-second>  
  12. </div>  
  13. </div>  

In the above lines of code we take a variable “componentVisible” in which we assign the component name that we want to make visible. If the value of the variable is “first” then “first” component will display, if the value of “componentVisible” is “second” then “second” component will display. I know this approach will fulfill our requirement but there are some issues that come around with this approach:

  • Increase the length of code for “App.component.html” file
  • Increase the if statements if we have more than 2 components to display on the condition bases.
  • It is not a good approach for coding because all the components are loading in initializing phase of “app” component that affect the performance of the application.

    Angular

The best approach is to load the component run time as we face the need. Loading the component dynamically increases the performance of our application and also increases the coding standard of the application. Let’s learn how to load any component dynamically.

App.component.html

  1. <!--The content below is only a placeholder and can be replaced.-->  
  2. <div style="text-align:center">  
  3.   <h1>  
  4.     Welcome to {{title}}!  
  5.   </h1>  
  6.   
  7.   <button type="button" (click)="visibleComponent('first')">First Component</button>  
  8.   <button type="button" (click)="visibleComponent('second')">Second Component</button>  
  9. <div>  
  10. <ng-template #template>  
  11. </ng-template>  
  12. </div>  
  13. </div>  

In the above lines of code we call the “visibleComponent” method of click event of both button and we send the component that we want to display as parameter of this method. In the last line of code we insert the “ng-template” tag that will work as a container of dynamic components.

App.component.ts

  1. import { Component,  
  2.   ViewChild,  
  3.   ViewContainerRef,  
  4.   ComponentFactoryResolver,  
  5.   ComponentRef,  
  6.   ComponentFactory,   
  7.   OnDestroy} from '@angular/core';  
  8. import { FirstComponent } from './first/first.component';  
  9. import { SecondComponent } from './second/second.component';  
  10.   
  11. @Component({  
  12.   selector: 'app-root',  
  13.   templateUrl: './app.component.html',  
  14.   styleUrls: ['./app.component.css']  
  15. })  
  16. export class AppComponent implements OnDestroy {  
  17.     
  18.   title = 'Advanced Angular App';  
  19.   componentRef_: any;  
  20.   @ViewChild('template', { read: ViewContainerRef }) container: ViewContainerRef;  
  21.   
  22.   constructor(private componentResolver: ComponentFactoryResolver){  
  23.   
  24.   }  
  25.   visibleComponent=(name:string)=>{  
  26.     this.container.clear();  
  27.     if(name=='first'){  
  28.       const componentFactory = this.componentResolver.resolveComponentFactory(FirstComponent);  
  29.       this.componentRef_ = this.container.createComponent(componentFactory);  
  30.       this.componentRef_.instance.message = 'This is First Component';  
  31.     }  
  32.     else if(name=='second'){  
  33.       const componentFactory = this.componentResolver.resolveComponentFactory(SecondComponent);  
  34.       this.componentRef_ = this.container.createComponent(componentFactory);  
  35.       this.componentRef_.instance.message = 'This is Second Component';  
  36.     }  
  37.   }  
  38.   
  39.   ngOnDestroy(){  
  40.       this.componentRef_.destroy();  
  41.   }  
  42.   
  43. }  

After updating the contents of “App.component.html” file now paste the above code into “app.component.ts” file. Now we read the above code line by line and understand how it will work.

In import section we write all the component and module names that we need to implement our requirement.

  1. componentRef_: any;  
  2. @ViewChild('template', { read: ViewContainerRef }) container: ViewContainerRef;  

In the above two lines of code we create “componentRef_” variable of type any, in this variable we will store the reference of our component either of “firstComponent” or “secondComponent”. In the second line of code we create an instance of “Viewchild” using this we will access the “ng-template” container in which we render the dynamically created component. Because “ng-template” will work as a container for our dynamic components, we define the type as “ViewContainerRef”. We also define the read property for “ViewChild” variable; this is because there can be several instances of various types associated with the element tag with the “#template” template variable and by default return type of the “ViewChild” is “elementRef”; so we define the read property of “ViewConatinerRef” to get the instance of “ViewConatiner” type.

  1. constructor(private componentResolver: ComponentFactoryResolver){  
  2.   }  

In constructor function we injected the “ComponentFactoryResolver” dependency. The “ComponentFactoryResolver” service will be used to resolve the component at run time. This service actually contains “resolveComponentFactory” method and using this method we create component at run time.

  1. visibleComponent=(name:string)=>{  
  2.     this.container.clear();  
  3.     if(name=='first'){  
  4.       const componentFactory = this.componentResolver.resolveComponentFactory(FirstComponent);  
  5.       this.componentRef_ = this.container.createComponent(componentFactory);  
  6.       this.componentRef_.instance.message = 'This is First Component';  
  7.     }  
  8.     else if(name=='second'){  
  9.       const componentFactory = this.componentResolver.resolveComponentFactory(SecondComponent);  
  10.       this.componentRef_ = this.container.createComponent(componentFactory);  
  11.       this.componentRef_.instance.message = 'This is Second Component';  
  12.     }  
  13.   }  

In the above code we create a “visibleComponent” method that we call from html section of component on click events of the button, this method takes a parameter that contains the component. The “clear” method of “ViewContainerRef” removes all content from the container and provides an empty and clean container to us. In the next line of code we use the “resolveComponentFactory” method of  “ComponentFactoryResolver” service, as explained earlier this method takes the component as input parameter and returns a component object that knows how to create a component.

If you log the “componentFactory” object you will get following details.

Angular

You can see that “ComponentFactory” contains a “create” method that knows how to create a component. This method will be used by the “CreateComponent” method of “ViewContainerRef” to create the component. In “createComponent” method of “ViewConatinRef” we pass the factory object that we created earlier and this method creates a component and inserts this component into template container and return type of the method is "ComponentRef".

The “instance” property of “ComponentRef” contains all property and method names available into that component and we can access these property and method names using the “instance” property.

Angular

If the value of “name” parameter is “first” then we insert the “FirstComponent” into our template container and if value is “second” then we insert the “SecondComponent” into “template container.

So far  98% of our tasks have been done to create the dynamic component. If we save all the changes and click on any button then we will get the following errors.

Angular

To resolve this error add the below line of code into you “app.module.ts” file.

“entryComponents:[FirstComponent,SecondComponent]”.

Angular
The “entryComponent” property contains the list of components that will be generated at run time. Adding the component to entryComponents tells the offline template compiler to compile the components so that they can be used at run time. Now save all the changes and click on “FirstComponent” button. As you click on this button the “First” Component will be loaded into you app component.

Angular

In the same way you can load the “Second” component dynamically by clicking on the “SecondComponent” button.

Angular

What is Component Factory?

Angular

In angular each component is generated by the factory and factory is generated by the compiler.  The compiler of angular takes the data defined into “@component” declaration. We know that each component contains the view and each view is constructed by several nodes of DOM elements. Each Dom node contains some specific information and content. So compiler at run time takes this information and provides it to the component factory to create a component.  Suppose we have a “third” component that has the following data.

third.component.html

  1. <p>  
  2.   third works!  
  3. </p>  

third.component.ts

  1. import { Component, OnInit } from '@angular/core';  
  2.   
  3. @Component({  
  4.   selector: 'app-third',  
  5.   templateUrl: './third.component.html',  
  6.   styleUrls: ['./third.component.css']  
  7. })  
  8. export class ThirdComponent implements OnInit {  
  9.   
  10.   constructor() { }  
  11.   ngOnInit() {  
  12.   }  
  13.   
  14. }  

As Angular compiler reads about data the following component factory will be generated.

  1. (function(jit_createRendererType2_0,jit_viewDef_1,jit_elementDef_2,jit_textDef_3  
  2. /*``*/) {  
  3. var styles_ThirdComponent = [''];  
  4. var RenderType_ThirdComponent = jit_createRendererType2_0({encapsulation:0,styles:styles_ThirdComponent,  
  5.     data:{}});  
  6. function View_ThirdComponent_0(_l) {  
  7.   return jit_viewDef_1(0,[(_l()(),jit_elementDef_2(0,0,null,null,1,'p',[],null,null,  
  8.       null,null,null)),(_l()(),jit_textDef_3(-1,null,['\n  third works!\n'])),(_l()(),  
  9.       jit_textDef_3(-1,null,['\n']))],null,null);  
  10. }  
  11.   
  12. return {RenderType_ThirdComponent:RenderType_ThirdComponent,View_ThirdComponent_0:View_ThirdComponent_0};  
  13. })  

Above data is the compiled form of the “third” component and used when component will  instantiated.  For each node there are two entries,  the first entry defines the element definition and the second entry defines the text content of node. If you want to check the “factory” for any component, you can find it in “sources” section of the browser.

Angular

Conclusion

Dynamic component is the best approach to load components at run time. If you have any questions then leave a comment in the comment section. Thanks for reading the article.