What are dynamic components?
Angular dynamic components are components that are not declared in the application's template at compile time but are instead loaded and rendered into the DOM at runtime. This allows for flexible and highly configurable user interfaces, where parts of the UI can be assembled or modified based on user actions, data, or application state.
What are Dynamic Components?
Unlike static components, which are hardcoded in a component's template, dynamic components are instantiated programmatically. Angular provides mechanisms to compile, resolve, and attach these components to a specific place in the DOM during the application's lifecycle, offering powerful capabilities for building complex and interactive applications.
When to use Dynamic Components?
- Building highly configurable dashboards where users can add, remove, or rearrange widgets.
- Implementing modal dialogs, popovers, or tooltips that appear on demand.
- Creating tabbed interfaces where tab content is loaded dynamically.
- Developing form builders or page builders that render components based on configuration.
- Plugins or extensibility architectures where external modules can contribute UI components.
Key Steps to Implement Dynamic Components
1. Defining the Dynamic Component
First, you need a regular Angular component that you intend to load dynamically. This component should be self-contained and ready to be rendered.
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-my-dynamic',
template: `
<div style="border: 1px solid blue; padding: 10px; margin-top: 10px;">
<h3>Dynamic Component</h3>
<p>{{ data }}</p>
<button (click)="onAction.emit('Dynamic action completed!')">Perform Action</button>
</div>
`
})
export class MyDynamicComponent {
@Input() data: string = '';
@Output() onAction = new EventEmitter<string>();
}
2. Registering as an Entry Component
For Angular to know how to create a component dynamically, it needs to be declared as an entryComponent in the NgModule where it's used. This tells the Angular compiler to create a ComponentFactory for it.
*Note: In Angular 9+, entryComponents is deprecated in favor of bootstrap or dynamic imports for standalone components. However, for understanding the concept and compatibility with older projects, it's often explicitly discussed. For standalone components, they are implicitly entry components.*
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyDynamicComponent } from './my-dynamic.component';
@NgModule({
declarations: [
AppComponent,
MyDynamicComponent // Declare your dynamic component
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent],
// Important: Register MyDynamicComponent as an entry component
entryComponents: [MyDynamicComponent]
})
export class AppModule { }
3. Locating a Host Element
You need a specific place in the DOM where the dynamic component will be inserted. This is typically achieved using an ng-template combined with ViewContainerRef. The ViewContainerRef represents a container where one or more views can be attached.
<!-- app.component.html -->
<button (click)="loadComponent()">Load Dynamic Component</button>
<ng-template #dynamicComponentHost></ng-template>
// app.component.ts (partial)
import { ViewChild, ViewContainerRef } from '@angular/core';
@ViewChild('dynamicComponentHost', { read: ViewContainerRef, static: true })
host!: ViewContainerRef;
4. Resolving and Creating the Component
ComponentFactoryResolver is used to get a ComponentFactory for the dynamic component. This factory is then used by ViewContainerRef to create an instance of the component and insert it into the host view.
// app.component.ts (partial)
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { MyDynamicComponent } from './my-dynamic.component';
@Component({ selector: 'app-root', template: `<!-- ... -->` })
export class AppComponent {
@ViewChild('dynamicComponentHost', { read: ViewContainerRef, static: true })
host!: ViewContainerRef;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
loadComponent() {
// Clear previous components if any
this.host.clear();
// 1. Resolve component factory for MyDynamicComponent
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(MyDynamicComponent);
// 2. Create component instance in the host view container
const componentRef = this.host.createComponent(componentFactory);
// 3. Interact with the component instance (e.g., pass inputs, subscribe to outputs)
componentRef.instance.data = 'Data passed dynamically!';
componentRef.instance.onAction.subscribe(message => {
console.log(message);
// Optionally destroy component after action
// componentRef.destroy();
});
}
}
Important Considerations
- Input/Output Communication: You can pass data to dynamic components using their
@Inputproperties and listen for events using@Outputproperties, accessed viacomponentRef.instance. - Change Detection: Dynamic components integrate into Angular's change detection mechanism automatically.
- Component Destruction: It's crucial to destroy dynamic components when they are no longer needed to prevent memory leaks. This can be done using
componentRef.destroy()orthis.host.clear(). - Performance: While powerful, creating and destroying many components dynamically can have performance implications if not managed carefully.
- Standalone Components: In modern Angular (v14+), standalone components are implicitly entry components and often do not require
ComponentFactoryResolverasViewContainerRef.createComponent()can directly take the component class.