DEV Community

Anastasios Theodosiou
Anastasios Theodosiou

Posted on

Exploring Dynamic Component Creation in Angular 16

In the wake of Angular's version 13 release, a fresh approach to dynamic component usage was unveiled, aiming to streamline the process. The question on everyone's mind: Did this update genuinely simplify dynamic component creation? To find out, we'll dive into the world of Angular version 16 and explore its newfound capabilities.


This article was originally published on Anastasios Theodosiou Blog.


Before we delve into the details, it's worth noting that in 2023, there are some intriguing Angular development tools you might want to explore alongside this dynamic component journey.

The core idea here is to harness Dynamic Parameters to determine which Angular Component should be dynamically generated. To achieve this, let's take a look at how our route configuration should be structured:

// Root Routes with lazy loaded Services Component
const routes: Routes = [
  // Other routes...
  {
    path: 'services/:id',
    loadChildren: () => import('./services/services.module').then((m) => m.ServicesModule),
  },
];
Enter fullscreen mode Exit fullscreen mode

In the route configuration above, the focus lies on line 6, where the dynamic parameter, ":id," is declared. This parameter will become accessible using the ActivatedRoute class, which is injected into the constructor via dependency injection. Here's an example of how you can access these dynamic parameters:

// Initial ServicesComponent implementation
import { ActivatedRoute } from '@angular/router';

// ...


constructor(private route: ActivatedRoute) {
  this.route.params.subscribe((params) => {
    // Access dynamic parameter here using params['id']
  });
}
Enter fullscreen mode Exit fullscreen mode

To avoid memory leaks, always remember to unsubscribe when your component is being destroyed, as not doing so can lead to issues. Now, let's move on to the core of our topic.

The Angular Team recommends creating a separate directive containing an injected ViewContainerRef class. This directive can then be reused across different components without the need for additional dependency injection in each component where dynamic components might be required. Here's an example implementation of such a directive:

// Example usage of ComponentHostDirective
<app-component-host></app-component-host>
Enter fullscreen mode Exit fullscreen mode

Angular 16 introduces a self-closing tag syntax similar to React's, which enhances code readability. Now that the directive is in place, you can use the @ViewChild decorator to access the publicly available viewContainerRef within your services.component class:

// Accessing viewContainerRef in services.component.ts
import { ComponentHostDirective } from '../directives/component-host.directive';
import { ViewChild } from '@angular/core';

export class ServicesComponent {
  @ViewChild(ComponentHostDirective) componentHost: ComponentHostDirective;

  // ...
}
Enter fullscreen mode Exit fullscreen mode

With the viewContainerRef in hand, you can proceed to create dynamic components. The method for handling component creation involves clearing any previously created component instances and then instantiating a new component based on the service id obtained from the servicesComponentFactory definition:

// Method to handle dynamic component creation
createDynamicComponent(serviceId: number) {
  this.componentHost.viewContainerRef.clear();
  const componentFactory = this.servicesComponentFactory.getServiceComponentFactory(serviceId);
  const componentRef = componentFactory.create(this.injector);
  this.componentHost.viewContainerRef.insert(componentRef.hostView);
}
Enter fullscreen mode Exit fullscreen mode

It's important to note that this implementation serves as an example and may not cover all use cases. You can customize it further to ensure proper mapping between parameters and component types.

The servicesComponentFactory definition might look something like this:

// Definition of servicesComponentFactory
import { Injectable } from '@angular/core';
import { ServiceTypes } from '../models/service-types.enum';

@Injectable({
  providedIn: 'root',
})
export class ServicesComponentFactory {
  getServiceComponentFactory(serviceId: number) {
    // Implement logic to return the appropriate component factory based on serviceId
  }
}
Enter fullscreen mode Exit fullscreen mode

In summary, your services.component.ts class should look similar to the one depicted above. With this setup, you can dynamically create child components based on the provided service id. As you can see, Angular 16 has made dynamic component creation appear quite straightforward, thanks to its latest features.

And there you have it! Dynamic component creation in Angular 16, with a fresh perspective and a cleaner approach. Kudos to the Angular Team for this update!

Find more in my personal blog here.

Top comments (2)

Collapse
 
kichnan profile image
Krishnan Mk

Have a look at the new createComponent API in @angular/core. And also ViewContainerRef.createComponent.

Collapse
 
zzpzaf profile image
Panos