Categories
Angular JavaScript

Loading Angular Components Dynamically

Spread the love

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at how to load and display Angular components dynamically.

Dynamic Component Loader

We can load components by using the ComponentFactoryResolver to load multiple dynamic components.

Also, we have to make a directive to host the components.

First, we have to create the following components and directives by running the following commands:

ng g directive BannerHost  
ng g component Foo  
ng g component Bar

Then we write the following code:

app.module.ts :

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { FooComponent } from './foo/foo.component';  
import { BarComponent } from './bar/bar.component';  
import { BannerHostDirective } from './banner-host.directive';

@NgModule({  
  declarations: [  
    AppComponent,  
    FooComponent,  
    BarComponent,  
    BannerHostDirective  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent],  
  entryComponents: [  
    FooComponent,  
    BarComponent,  
  ]  
})  
export class AppModule { }

banner-host.directive.ts :

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({  
  selector: '[banner-host]'  
})  
export class BannerHostDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

bar.component.html :

bar

foo.component.ts :

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

@Component({  
  selector: 'app-foo',  
  templateUrl: './foo.component.html',  
  styleUrls: ['./foo.component.css']  
})  
export class FooComponent implements OnInit {  
  @Input() data: string; constructor() { } 

  ngOnInit() {  
  }
}

foo.component.html :

foo {{data}}

app.component.ts :

import { Component, ComponentFactoryResolver, ViewChild } from '@angular/core';  
import { FooComponent } from './foo/foo.component';  
import { BannerHostDirective } from './banner-host.directive';  
import { BarComponent } from './bar/bar.component';

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  interval: any;  
  currentBannerIndex: number = -1;  
  banners = [FooComponent, BarComponent]  
  @ViewChild(BannerHostDirective, { static: true }) bannerHost: BannerHostDirective;  

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { } 

  ngOnInit() {  
    this.loadComponent();  
    this.getBanners();  
  } ngOnDestroy() {  
    clearInterval(this.interval);  
  }

  loadComponent() {  
    this.currentBannerIndex = (this.currentBannerIndex + 1) % this.banners.length;  
    const bannerItem = this.banners[this.currentBannerIndex];
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(bannerItem);  
    const viewContainerRef = this.bannerHost.viewContainerRef;  
    viewContainerRef.clear();  
    const componentRef = viewContainerRef.createComponent(componentFactory);  
    if (componentRef.instance.constructor === FooComponent) {  
      (<FooComponent>componentRef.instance).data = 'hello';  
    }  
  } 

  getBanners() {  
    this.interval = setInterval(() => {  
      this.loadComponent();  
    }, 3000);  
  }  
}

app.component.html :

<ng-template banner-host></ng-template>

In the code above, we created the banner-host directive to inject the component that we’re going to load.

We applied that to ng-template in app.component.html .

FooComponent takes a data input that we’re going to set.

Then the components are loaded in AppComponent .

The loadComponent method in AppComponent by going through the banners that we defined in the banners array.

We changed the currentBannerIndex every 3 seconds as indicated by the getBanners method.

The actual component loading is done by the following code:

const componentFactory = this.componentFactoryResolver.resolveComponentFactory(bannerItem);  
const viewContainerRef = this.bannerHost.viewContainerRef;  
viewContainerRef.clear();  
const componentRef = viewContainerRef.createComponent(componentFactory);  
if (componentRef.instance.constructor === FooComponent) {  
  (<FooComponent>componentRef.instance).data = 'hello';  
}

In the code above, we run the this.componentFactoryResolver.resolveComponentFactory method with the bannerItem as set by:

const bannerItem = this.banners[this.currentBannerIndex];

Then we clear the current view by writing:

const viewContainerRef = this.bannerHost.viewContainerRef;  
viewContainerRef.clear();

Next, we load the component by writing:

const componentRef = viewContainerRef.createComponent(componentFactory);

Finally, we check that the component is an instance of the FooComponent . If it is, then we load the string data property as indicated in the data field of FooComponent as follows:

if (componentRef.instance.constructor === FooComponent) {  
  (<FooComponent>componentRef.instance).data = 'hello';  
}

We used componentRef.instance.constructor to get which component instance is loaded.

Then if the component is an instance of FooComponent , we cast it to the FooComponent and set the data property of it to 'hello' as follows:

(<FooComponent>componentRef.instance).data = 'hello';

In the end, we should see foo hello and bar displayed one after the other in a 3-second cycle.

Conclusion

Displaying multiple Angular components dynamically involves several steps.

First, we have to create a directive to inject the components that we want to load.

Then we have to add an ng-template with the directive that we just created applied to it.

Next, we clear the existing view and use the ComponentFactoryResolver to clear the screen and then load the component that we want to load.

We can check the instance of the component that’s loaded and then do different things according to what component instance is loaded.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *