Day 9: Dynamic Component Loading: From ViewContainerRef to Modern APIs
Leading a frontend team or mentoring devs? This is part of our advanced Angular series. Explore more at justcompile.com/angular or reach out at trainings@justcompile.com.
Introduction
Typically, the components in our Angular application are static. We declare them in our templates (<app-user-profile>), and the Angular compiler wires everything together at build time. But what if you don't know which component to render until runtime?
This is where dynamic component loading comes in. It's the process of loading and rendering a component programmatically based on some condition—a user's click, data from an API, or a configuration object. This technique is the foundation for many advanced features, such as:
Let's explore how this is done using Angular's modern APIs.
The Deep-Dive: Building a Dynamic Ad Banner
Our goal is to create a component that cycles through different types of ads, loading the appropriate ad component dynamically.
Step 1: The Anchor Point
First, we need a place in our template to tell Angular where the dynamic component should be inserted. We'll create a simple directive to act as an anchor.
ad-host.directive.ts
TypeScript
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[adHost]',
standalone: true // Modern directives are standalone!
})
export class AdHostDirective {
// We inject ViewContainerRef to get access to the view container of this element.
constructor(public viewContainerRef: ViewContainerRef) { }
}
This directive doesn't do anything on its own; its only job is to give us a stable way to get a reference to a ViewContainerRef, which is a container where views (and components) can be attached.
Step 2: The Host Component
Now, let's create the component that will host our dynamic ads.
ad-banner.component.ts
Recommended by LinkedIn
TypeScript
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdHostDirective } from './ad-host.directive';
import { HeroAdComponent } from './hero-ad.component';
import { ProductAdComponent } from './product-ad.component';
@Component({
selector: 'app-ad-banner',
standalone: true,
imports: [CommonModule, AdHostDirective, HeroAdComponent, ProductAdComponent],
template: `
<div class="ad-banner">
<h3>Advertisements</h3>
<ng-template adHost></ng-template>
</div>
`
})
export class AdBannerComponent implements OnInit, OnDestroy {
// Get a reference to the element where our adHost directive is applied.
@ViewChild(AdHostDirective, { static: true }) adHost!: AdHostDirective;
private ads = [HeroAdComponent, ProductAdComponent];
private interval: number | undefined;
ngOnInit(): void {
this.loadComponent();
this.getAds();
}
ngOnDestroy(): void {
clearInterval(this.interval);
}
loadComponent(index = 0) {
// 1. Get the ViewContainerRef from our host directive
const viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear(); // Clear any previous component
// 2. Create the component directly! (No factory needed)
const componentRef = viewContainerRef.createComponent(this.ads[index]);
// 3. (Optional) Pass data to the component
// componentRef.instance.data = adItem.data;
}
private getAds() {
this.interval = window.setInterval(() => {
const nextIndex = (this.ads.indexOf(componentRef.componentType) + 1) % this.ads.length;
this.loadComponent(nextIndex);
}, 3000);
}
}
Key Steps in the Modern API:
A Note on the "Classic" Approach
Before Angular 9 (Ivy), this process was more complex. You had to:
Thankfully, this is no longer necessary, making the process much more intuitive.
From Theory to Team Mastery
The ability to load components dynamically is a gateway to building truly sophisticated user interfaces. At Just Compile, we find this is a critical skill for teams building enterprise-grade applications. A quintessential example we explore in our advanced workshops is creating a fully configurable dashboard.
Imagine a dashboard defined by a JSON object from a server. This JSON specifies which widgets to display ('ChartWidget', 'DataTableWidget', 'ActivityFeedWidget') and their configurations. The main dashboard component parses this JSON, maintains a mapping between widget names and their component types (e.g., 'ChartWidget' -> ChartWidgetComponent), and then uses viewContainerRef.createComponent() to render the entire dashboard. This creates an incredibly flexible system where the UI can be completely altered without a new front-end deployment. Mastering this pattern empowers developers to solve complex business requirements with elegant, scalable solutions.
Practical Takeaways
Join the Conversation
What are some real-world scenarios where you've used or could use dynamic component loading? Share your ideas in the comments!
Want to equip your team to build scalable and maintainable component libraries from the ground up? Get in touch with Just Compile for specialized corporate training on Angular and various other technologies.
For training inquiries: trainings@justcompile.com Website: www.justcompile.com/angular