Home  >  Angular 2

Angular 2/4 Dynamic Component Loader Example

By Arvind Rai, July 23, 2017
This page will walk through Angular dynamic component loader example. Generally a component is loaded using component selector in component template that is identified at Angular compile time. Component can also be loaded dynamically at runtime using ComponentFactory, ComponentFactoryResolver and ViewContainerRef. Those components which need to be loaded dynamically should also be configured in entryComponents metadata of @NgModule decorator in module file. To load a dynamic component in a template we need an insert location and to get it we need ViewContainerRef of a decorator or a component. Find the code snippet to load dynamic component.
loadComponent(viewContainerRef: ViewContainerRef, postItem: PostItem) {
	let componentFactory = this.componentFactoryResolver
                      .resolveComponentFactory(postItem.component);
	viewContainerRef.clear();
	let componentRef = viewContainerRef.createComponent(componentFactory);
	let myPost: MyPost = <MyPost>componentRef.instance;
	myPost.post = postItem.data;
} 
Here on this page we will create a banner that will show highlights of posts. For insert location we will use directive as well as component. Now find the complete example step by step.

Technologies Used

Find the technologies being used in our example.
1. Angular 4.2.0
2. TypeScript 2.3.3
3. Node.js 6.10.1
4. Angular CLI 1.2.0
5. Angular Compiler CLI 4.2.0

Project Structure

Find the project structure of our demo application.
angular-demo
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--mypost.ts   
|   |   |--article.component.ts   
|   |   |--technology.component.ts   
|   |   |--mypost.service.ts   
|   |   |--mypost.directive.ts   
|   |   |--mypost.component.ts   
|   |   |--post-item.ts   
|   |   |--mypost-banner.component.ts   
|   |   |--app.component.ts
|   |   |--app.module.ts
|   |   
|   |--main.ts
|   |--index.html
|   |--styles.css
|
|--node_modules
|--package.json

ComponentFactory and ComponentFactoryResolver

ComponentFactory is used to create instance of components. ComponentFactoryResolver resolves a ComponentFactory for a specific component. It is used as follows.
let componentFactory = this.componentFactoryResolver
                  .resolveComponentFactory(component); 

ViewContainerRef

ViewContainerRef represents a container where we can attach one or more views. Some important methods of ViewContainerRef are createEmbeddedView() and createComponent(). createEmbeddedView() is used to attach embedded views based on TemplateRef. createComponent() instantiates a single component and inserts its host view into the view container at a specified index. In our dynamic component loader example, we will load component using createComponent() of ViewContainerRef.
let componentRef = viewContainerRef.createComponent(componentFactory); 
We get a ComponentRef of newly created component as a return of the above method.
clear() method of ViewContainerRef destroys existing view in the container.

Dynamic Component Loader Example

We will create an example of dynamic component loader for post highlights. We will create two components that will be loaded dynamically in view container. We can get view container of insert location using directive and component. We will use setInterval() to load components in view container with an interval to show post highlights.

Step-1: Create Components to Load Dynamically

For the dynamic component example, we will create two components that will be loaded dynamically. Both components will implement an interface. Find the interface.
mypost.ts
export interface MyPost {
  post: any;
} 
Find the components now. In the components we have a variable decorated with @Input() that will be populated when component will be loaded dynamically.
technology.component.ts
import { Component, Input } from '@angular/core';

import { MyPost } from './mypost';

@Component({
  template: `
        <div class="post-highlights1">
	  <h3>Technology: {{post.name}}</h3>
     	  <b>Description:</b>
          <p>{{post.description}}</p>
	</div>
  `  
})
export class TechnologyComponent implements MyPost {
   @Input() post: any;
} 
article.component.ts
import { Component, Input } from '@angular/core';

import { MyPost } from './mypost';

@Component({
  template: `
        <div class="post-highlights2">
            <p><b>Article-{{post.sn}}: {{post.title}}</b></p>
            <p>Category: {{post.category}}</p>
	</div>
  `
})
export class ArticleComponent implements MyPost {
   @Input() post: any;
} 

Step-2: Create Service for Dynamic Component Loading

In the service we are creating methods for getting all posts and to load component for post highlights. Find the service class.
mypost.service.ts
import { Injectable, ComponentFactoryResolver, ViewContainerRef } from '@angular/core';

import { ArticleComponent }  from './article.component';
import { TechnologyComponent }  from './technology.component';
import { PostItem } from './post-item';
import { MyPost } from './mypost';

@Injectable()
export class MyPostService { 
        constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
	
	loadComponent(viewContainerRef: ViewContainerRef, postItem: PostItem) {
		let componentFactory = this.componentFactoryResolver
		                      .resolveComponentFactory(postItem.component);
		viewContainerRef.clear();
		let componentRef = viewContainerRef.createComponent(componentFactory);
		let myPost: MyPost = <MyPost>componentRef.instance;
		myPost.post = postItem.data;
	}
	getAllPosts() {
		return [
		  new PostItem(TechnologyComponent, {name: 'Angular 2', 
		      description: 'Angular is a platform that makes it easy to build applications with the web.'}),

		  new PostItem(TechnologyComponent, {name: 'Spring Boot', 
		      description: 'Spring Boot makes it easy to create stand-alone, production-grade applications.'}),

		  new PostItem(ArticleComponent, {sn: "1", 
		      title: 'Angular 2 Routing and Navigation Example', category: 'Angular 2'}),
			  
		  new PostItem(ArticleComponent, {sn: "2", 
		      title: 'Angular 2 Template Reference Variable Example', category: 'Angular 2'}),
			  
		  new PostItem(ArticleComponent, {sn: "3", 
		      title: 'Spring Boot Custom Banner Example', category: 'Spring Boot'}),
			  
		  new PostItem(ArticleComponent, {sn: "4", 
		      title: 'Spring Boot Change Default Server Port', category: 'Spring Boot'})										
		];
        }
} 
In the loadComponent() method, we are instantiating ComponentFactory for the given component. Now we will create dynamic component. Before creating it we should clear the existing dynamic component from view container using clear() method of ViewContainerRef. Now create dynamic component using createComponent() method of ViewContainerRef that will return instance of ComponentRef for the newly loaded dynamic component. To pass any data to dynamic component use instance property of ComponentRef.
In the getAllPosts() method we have prepared data for post highlights. We have an array of PostItem that will contain dynamic component name and their corresponding data to be loaded. Now find the code of PostItem class.
post-item.ts
import { Type } from '@angular/core';

export class PostItem {
  constructor(public component: Type<any>, public data: any) {}
} 

Step-3: Create Directive and Component to get Dynamic Component Insert Location

Dynamic component will be loaded in the given insertion place. Insertion place will be view container. To get view container reference we can use either directive or component that will do dependency injection of ViewContainerRef using constructor and keep scope public. If we want to use instance of ViewContainerRef of directive or component in any other component then we use @ViewChild() decorator and ultimately we can access their instance of ViewContainerRef. In our example we will use directive and component both to get insertion location for dynamic component. Find the directive.
mypost.directive.ts
import { Directive, ViewContainerRef } from '@angular/core';

@Directive({ 
   selector: '[cpMyPost]' 
})
export class MyPostDirective {
   constructor(public viewContainerRef: ViewContainerRef) { }
} 
Directive is used as following in any component template to get insertion location.
<div cpMyPost></div> 
Now find the component.
mypost.component.ts
import { Component, ViewContainerRef } from '@angular/core';

@Component({
  selector: 'app-mypost',
  template: ''
})
export class MyPostComponent {
   constructor(public viewContainerRef: ViewContainerRef) { }
} 
Component is used as following in any component template to get insertion location.
<app-mypost></app-mypost> 

Step-4: Configure Dynamic Components using entryComponents in Module

When we give a selector reference of a component in a template of another component then the Angular compiler generates ComponentFactory automatically for that component. But in the case of dynamic component loading there is no selector reference in the template of another component. To ensure that compiler still generates ComponentFactory, we need to configure dynamic components in entryComponents metadata of @NgModule decorator in module file.
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';
import { MyPostBannerComponent }  from './mypost-banner.component';
import { ArticleComponent }  from './article.component';
import { TechnologyComponent }  from './technology.component';
import { MyPostComponent }  from './mypost.component';
import { MyPostDirective }  from './mypost.directive';
import { MyPostService }  from './mypost.service';

@NgModule({
  imports: [     
        BrowserModule
  ],
  declarations: [
        AppComponent,
	MyPostBannerComponent,
	ArticleComponent,
	TechnologyComponent,
	MyPostComponent,
	MyPostDirective		
  ], 
  entryComponents: [ 
        ArticleComponent, 
	TechnologyComponent 
  ], 
  providers: [ 
        MyPostService
  ],  
  bootstrap: [
        AppComponent
  ]
})
export class AppModule { } 

Step-5: Create Component to Initiate Dynamic Component Loading

To use dynamic component loading, we will create a component and in its template we will give insert location using directive and component. We will also fetch their instances to get ViewContainerRef for dynamic component loading in the view container located by them. The instance of these directive and component will be obtained using @ViewChild() decorator.
mypost-banner.component.ts
import { Component, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';

import { MyPostDirective } from './mypost.directive';
import { MyPostComponent } from './mypost.component';
import { MyPostService } from './mypost.service';
import { PostItem } from './post-item';

@Component({
   selector: 'post-banner',
   template: `
     <h3>Post Highlights using ViewContainerRef from Directive</h3>
     <div cpMyPost></div>
     <h3>Post Highlights using ViewContainerRef from Component</h3>
     <app-mypost></app-mypost>
   `
})
export class MyPostBannerComponent implements AfterViewInit, OnDestroy { 

    @ViewChild(MyPostDirective)
    private myPostDirective: MyPostDirective;	
	
    @ViewChild(MyPostComponent)
    private myPostComponent: MyPostComponent;	
	
    postItems: PostItem[];
    intervalId: any;	
    postIndex: number = -1;
	
    constructor(private myPostService: MyPostService) { }
	
    ngAfterViewInit() {
       this.postItems = this.myPostService.getAllPosts(); 	
       this.startPostHighlights();
    }	
    startPostHighlights() {
       this.intervalId = setInterval(() => {
		   
    	 this.postIndex = (this.postIndex === this.postItems.length)? 0 : this.postIndex + 1;

	 //Use viewContainerRef from Directive
         this.myPostService.loadComponent(this.myPostDirective.viewContainerRef, this.postItems[this.postIndex]);
		 
	 //Use viewContainerRef from Component
	 this.myPostService.loadComponent(this.myPostComponent.viewContainerRef, this.postItems[this.postIndex]);

       }, 2000);
    }	
    ngOnDestroy() {
       clearInterval(this.intervalId);
    }	
} 
Within the setInterval() method, we will call loadComponent() method of our MyPostService class and pass instance of ViewContainerRef and instance of our PostItem. In ngOnDestroy() method we will clear interval using clearInterval() method.
Now find the application component.
app.component.ts
import { Component } from '@angular/core';

@Component({
   selector: 'app-root',
   template: `
		<post-banner></post-banner>	
             `
})
export class AppComponent { 
} 

Run Demo Application

To run the demo application, find following steps.
1. Download source code using download link given below on this page.
2. In your angular CLI application, replace src folder from the downloaded one.
3. Run ng serve using command prompt.
4. Now access the URL http://localhost:4200
Angular 2/4 Dynamic Component Loader Example

References

Dynamic Component Loader
Angular 2 @ViewChild() Example

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
FIND MORE TUTORILAS





Copyright ©2017 concretepage.com, all rights reserved |Privacy Policy | Contact Us