How to Lazy and Dynamically Load a Component in Angular
Tags: angular, webdev, javascript
Published: 2023-04-21 04:53:26

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);
  }
} 
```
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);
   }
}
```
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 <app-greet> 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;
```

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);
      })

```
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);
      })
    }
 
  }
}

```

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.