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 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.
Top comments (3)
Instead of this.greetcomp.instance.message, you should call setInput
Can you provide me with more detail on the same ? When can I read more about that?
I use the same approach, only thing is, I can’t get change detection working on the dynamically created component.