Dynamic Component Loading with Angular’s `ViewContainerRef`:A Comprehensive Guide

Brajraj Agrawal
4 min readSep 8, 2024

--

In modern web applications, especially single-page applications (SPAs), there is often a need to dynamically create, insert, or remove components based on user actions, preferences, or business logic. Angular provides a powerful tool for this purpose: ViewContainerRef. In this article, we'll explore various real-world use cases where ViewContainerRef is the only viable solution, and we'll walk through a detailed code example demonstrating how to dynamically load components in Angular.

Why Use ViewContainerRef?

ViewContainerRef provides a way to interact with the DOM and manipulate view hierarchies. It allows you to programmatically:

  • Insert, move, or remove views within a ViewContainer.
  • Dynamically load components at runtime.
  • Manage component lifecycles manually.

This is especially useful in scenarios like:

  • Dynamic dialogs and modals
  • Customizable dashboards
  • Drag-and-drop UI builders
  • Lazy loading heavy components
  • Conditional rendering based on complex business logic

Real-World Use Case: Dynamic Widget Loading in a Dashboard

Let’s build a dynamic widget-loading feature in an admin dashboard. In this example, users can add different types of widgets to their dashboard (like a weather widget, news feed widget, and a stock ticker widget) based on their preferences.

Step-by-Step Implementation

1. Set Up the Angular Project

First, create a new Angular project and add the necessary components.

ng new dynamic-widget-dashboard
cd dynamic-widget-dashboard
cd src/app
mkdir widgets
cd widgets
ng generate component widgets/weather-widget
ng generate component widgets/news-widget
ng generate component widgets/stock-widget

2. Create the Widget Components

Each widget represents a separate Angular component. Let’s define three simple widget components: WeatherWidgetComponent, NewsWidgetComponent, and StockWidgetComponent.

weather-widget.component.ts

import { Component } from '@angular/core';

@Component({
selector: 'app-weather-widget',
template: `<div class="widget">Weather Widget Content</div>`,
styles: ['.widget { padding: 10px; border: 1px solid #ddd; margin-bottom: 10px; }']
})
export class WeatherWidgetComponent {}

news-widget.component.ts

import { Component } from '@angular/core';

@Component({
selector: 'app-news-widget',
template: `<div class="widget">News Widget Content</div>`,
styles: ['.widget { padding: 10px; border: 1px solid #ddd; margin-bottom: 10px; }']
})
export class NewsWidgetComponent {}tystock-widget.component.ts`

stock-widget.component.ts

import { Component } from '@angular/core';

@Component({
selector: 'app-stock-widget',
template: `<div class="widget">Stock Ticker Widget Content</div>`,
styles: ['.widget { padding: 10px; border: 1px solid #ddd; margin-bottom: 10px; }']
})
export class StockWidgetComponent {}

3. Use ViewContainerRef to Dynamically Load Components

Now, let’s create a DashboardComponent that allows users to add these widgets dynamically. We will use ViewContainerRef to load components into a specific location within the template.

dashboard.component.ts

import { Component, inject, ViewChild, ViewContainerRef } from '@angular/core';
import { WeatherWidgetComponent } from '../widgets/weather-widget/weather-widget.component';
import { NewsWidgetComponent } from '../widgets/news-widget/news-widget.component';
import { StockWidgetComponent } from '../widgets/stock-widget/stock-widget.component';
import { FormsModule } from '@angular/forms';

@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
standalone: true,
imports: [FormsModule]
})
export class DashboardComponent {
// Reference to the custom placeholder where components will be dynamically loaded
@ViewChild('customPlaceholder', { read: ViewContainerRef, static: true }) customPlaceholder!: ViewContainerRef;

widgetSelections = {
weather: false,
news: false,
stock: false,
};

vcr = inject(ViewContainerRef);

loadSelectedWidgets() {
// Clear previously loaded widgets from the placeholder
this.customPlaceholder.clear();

// Load selected widgets dynamically into the placeholder
if (this.widgetSelections.weather) {
this.loadWidget(WeatherWidgetComponent);
}
if (this.widgetSelections.news) {
this.loadWidget(NewsWidgetComponent);
}
if (this.widgetSelections.stock) {
this.loadWidget(StockWidgetComponent);
}
}

private loadWidget(component: any) {
this.customPlaceholder.createComponent(component);
}
}

dashboard.component.html

<div class="dashboard">
<h2>Dynamically load Components using <code style="background-color: rgb(224, 224, 224);">ViewContainerRef</code></h2>
<div class="widget-selection">
<label>
<input type="checkbox" [(ngModel)]="widgetSelections.weather" /> Weather Widget
</label>
<label>
<input type="checkbox" [(ngModel)]="widgetSelections.news" /> News Widget
</label>
<label>
<input type="checkbox" [(ngModel)]="widgetSelections.stock" /> Stock Ticker Widget
</label>
</div>
<button (click)="loadSelectedWidgets()">Load Selected Widgets</button>

<!-- Custom Placeholder for Dynamic Widgets -->
<div class="custom-placeholder">
<ng-container #customPlaceholder></ng-container>
</div>
</div>

4. Explanation of the Code

  • Dynamic Widget Loading:
    -
    The component manages the dynamic loading of widgets based on user selections.
  • ViewContainerRef:
    - @ViewChild decorator with ViewContainerRef type retrieves a reference to a container element in the template (customPlaceholder).The { read: ViewContainerRef } option ensures we get a reference to the container.
    - vcr property (using inject) is another way to access the default view container.
  • Widget Selection State:
    widgetSelections object stores booleans indicating if each widget (weather, news, stock) is selected.
  • loadSelectedWidgets Method:
    -
    Clears any previously loaded widgets from the customPlaceholder.
    - Iterates through widgetSelections and loads the corresponding widget component dynamically if selected.
  • loadWidget Method (Private):
    -
    Takes a component class as input (WeatherWidgetComponent, NewsWidgetComponent, etc.).
    - Uses customPlaceholder.createComponent to dynamically create an instance of the provided component within the container.

5. Add Some Styling

To make our dashboard look better, we can add some CSS.

dashboard.component.css

.dashboard {
padding: 20px;
border: 1px solid #ccc;
max-width: 600px;
margin: 20px auto;
text-align: center;
}

button {
margin: 5px;
padding: 10px;
font-size: 14px;
}

.widget {
margin-top: 20px;
}

6. Run the Application

To see the dynamic widget loading in action, run the Angular application.

ng serve

Visit http://localhost:4200 in your browser, and you should see the dashboard interface. You can add different widgets dynamically by clicking the buttons.

Benefits of Using ViewContainerRef

  1. Flexibility: Provides full control over dynamically loaded components, allowing for complex and customizable UIs.
  2. Performance Optimization: Only loads components when needed, which can improve performance and reduce the initial load time.
  3. Encapsulation: Keeps different parts of the UI encapsulated within their components, improving maintainability and reusability.

Conclusion

Using ViewContainerRef to dynamically load components provides flexibility in Angular applications, allowing for modular and dynamic user interfaces. For more detailed code and a working example, be sure to check out the GitHub repository.

Feel free to reach out to me on LinkedIn if you have any questions or need further clarification.

--

--

Brajraj Agrawal
Brajraj Agrawal

Written by Brajraj Agrawal

Words that Build | Full-Stack Developer (MEAN Stack). Turning ideas into web apps for 8+ years.

No responses yet