DEV Community

Dhananjay Kumar
Dhananjay Kumar

Posted on

12

How to Lazy and Dynamically Load a Component in Angular

There are three ways you can use a component in an Angular app.

  • Loading on route change
  • Using as a child component
  • Dynamically loading on demand

However, this article focuses on loading a component dynamically and lazily. The main advantage of lazily loading a component is reducing the initial bundle size and only downloading the component in the browser when required.
 
Let us say that you have a component called GreetComponent, as shown in the next code block,

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
 
const template = `
   <h2>{{message}} </h2>
   <button (click)='sendMessage()'>Send Message </button>
`
@Component({
  selector: 'app-greet',
  standalone: true,
  imports: [CommonModule],
  template: template
})
export class GreetComponent {
  @Input({ required: true }) message?: string;
  @Output() messageEvent = new EventEmitter<boolean>();
  sendMessage(): void {
    this.messageEvent.emit(true);
  }
} 
Enter fullscreen mode Exit fullscreen mode

The GreetComponent has an @Input decorated property and an @Output() decorated EvenEmmiter. The FooComponent uses it as a child component, as shown in the following code block.

const template = `
   <app-greet [message]='message' (messageEvent)='sendMessage($event)'></app-greet>
`
@Component({
  selector: 'app-foo',
  standalone: true,
  imports: [CommonModule,
    GreetComponent],
  template : template
})
export class FooComponent {
   message = "data from parent"
 
   sendMessage(m:boolean){
      console.log(m);
   }
}
Enter fullscreen mode Exit fullscreen mode

A couple of points worth noticing here are,

  • GreetComponent is part of the imports array.
  • GreetComponent is used on the template.

Due to the above two points, whenever Angular compiles FooComponent, it also includes GreetComponent, increasing the size of the bundle containing the FooComponent.
 
 
*One main advantage of loading a component dynamically (lazily) is that it reduces the bundle size because it only gets downloaded in the browser when required. *
 
 
Let us say that,  GreetComponent should be loaded dynamically and lazily with the click of a button in the FooComponent. For that, 

  • Add a button
  • Remove from the template of FooComponent
  • Remove GreetComponent from the imports array. [Important]
const template = `
   <button (click)='loadComponent()'>Load Greet Component </button>
`
@Component({
  selector: 'app-foo',
  standalone: true,
  imports: [CommonModule],
  template: template
})
export class FooComponent {
  message = "data from parent"
  greetcomp: any;
Enter fullscreen mode Exit fullscreen mode

To dynamically load the component, inject the ViewContainerRef using the inject function or the constructor injection.  

vcr = inject(ViewContainerRef);

After that, import the file that contains GreetComponent using the import statement.  The import statement is used in JavaScript to load a file dynamically.

const { GreetComponent } = await import('../greet/greet.component');

After importing the file, use the CreateComponent method of ViewContainerRef.

this.greetcomp = this.vcr.createComponent(GreetComponent);

You can access all properties and events of the dynamically loaded components using the instance method. So the a value can be passed to the message property as shown in the next code block,

this.greetcomp.instance.message = "Hello dynamic Component";

You can subscribe to the @Output decorated EventEmitter as shown next,

this.greetcomp.instance.messageEvent.subscribe((data:any)=>{
        console.log(data);
      })

Enter fullscreen mode Exit fullscreen mode

Putting everything together, you can lazy load a GreetComponent with the click of a button in the FooComponent, as shown in the following code listing,

const template = `
   <button (click)='loadComponent()'>Load Greet Component </button>
`
@Component({
  selector: 'app-foo',
  standalone: true,
  imports: [CommonModule],
  template: template
})
export class FooComponent {
  message = "data from parent"
  greetcomp: any;
  vcr = inject(ViewContainerRef);
  
  async loadComponent() {
    this.vcr.clear();
    const { GreetComponent } = await import('../greet/greet.component');
    this.greetcomp = this.vcr.createComponent(GreetComponent);
    if (this.greetcomp) {
      this.greetcomp.instance.message = "Hello dynamic Component";
 
      this.greetcomp.instance.messageEvent.subscribe((data:any)=>{
        console.log(data);
      })
    }
 
  }
}

Enter fullscreen mode Exit fullscreen mode

Currently, Angular loads GreetComponent at the end after all DOM elements of FooComponent. To load it at a specific div block, read the div block as ViewChild and ViewConatinerRef.  In the other post, I will cover that in detail.
 
I hope you find this post helpful. Thanks for reading.

SurveyJS custom survey software

Build Your Own Forms without Manual Coding

SurveyJS UI libraries let you build a JSON-based form management system that integrates with any backend, giving you full control over your data with no user limits. Includes support for custom question types, skip logic, an integrated CSS editor, PDF export, real-time analytics, and more.

Learn more

Top comments (3)

Collapse
 
lifekefunde profile image
Gaurav Mukherjee

Instead of this.greetcomp.instance.message, you should call setInput

Collapse
 
debug_mode profile image
Dhananjay Kumar

Can you provide me with more detail on the same ? When can I read more about that?

Collapse
 
johan_vrolix_6724e19ce9ce profile image
Johan Vrolix

I use the same approach, only thing is, I can’t get change detection working on the dynamically created component.

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs