🅰️ Angular Q58 / 125

What are dynamic components?

AI-Powered Answer ✓ Answered

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.

typescript
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.*

typescript
// 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.

html
<!-- app.component.html -->
<button (click)="loadComponent()">Load Dynamic Component</button>
<ng-template #dynamicComponentHost></ng-template>
typescript
// 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.

typescript
// 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 @Input properties and listen for events using @Output properties, accessed via componentRef.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() or this.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 ComponentFactoryResolver as ViewContainerRef.createComponent() can directly take the component class.