Day 9: Dynamic Component Loading: From ViewContainerRef to Modern APIs

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:

  • Building a dashboard with user-configurable widgets.
  • Creating a modal/dialog service that can render any component inside a pop-up.
  • Rendering different ad banners based on a user's profile.

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

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:

  1. We use @ViewChild to get a reference to our AdHostDirective.
  2. We access the viewContainerRef property of the directive instance.
  3. We call viewContainerRef.clear() to ensure we don't stack components on top of each other.
  4. We call viewContainerRef.createComponent() and pass the component type (HeroAdComponent) directly. This is the magic of the modern Ivy compiler.

A Note on the "Classic" Approach

Before Angular 9 (Ivy), this process was more complex. You had to:

  1. Inject ComponentFactoryResolver.
  2. Use it to resolve a ComponentFactory for your component type.
  3. Pass that factory to viewContainerRef.createComponent().
  4. Explicitly list all dynamically loaded components in your NgModule's entryComponents array.

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

  • Dynamic component loading allows you to render components programmatically at runtime.
  • Use a directive as a stable anchor point in your template to get a ViewContainerRef.
  • The modern Angular API is simple: get the ViewContainerRef and call .createComponent(YourComponentType).
  • This technique is ideal for data-driven UIs like configurable dashboards, modal services, and content-rendering systems.

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

To view or add a comment, sign in

Others also viewed

Explore content categories