Dynamically add components to the DOM with Angular

Attaching a component dynamically to the DOM with Angular can be tricky if you don’t read extensively the documentation. I’ll try to keep this tutorial pretty straightforward and include some notes that both the official and unofficial documentation lack of mentioning, or at the very least they aren’t that clear about it.

First of all I’d like to mention that I’m not an Angular expert and what I’m about to describe might not be the only way to create dynamic components, but it was the one I found was the easiest and suitable for the development in which I needed this functionality.

Side note: This tutorial is gonna focus in the creation of a component from a service (See Use Cases section), although, the code can be used from within another component.

The code shown here is written in ES6. I’ve created a Gist in ES6 and a Plunker in Typescript.

Update: [Dynamic children components] I’ve added another section at the bottom to illustrate how to dynamically append a component as a child. (here’s the Plunker)

The “ViewContainerRef”

Represents a container where one or more Views can be attached.

I’ll focus in the use of the ViewContainerRef to attach a component. This object is basically a $(<selector>) for Angular (DOM manipulation). Injecting this object into a Component’s constructor will give us that Component’s container and, with it, we can append a sibling Component to that container.

Let’s get cracking

1. Dynamic Component

First of all, we’ll create the Component to be dynamically added to the DOM:

import { Component } from '@angular/core'
@Component({
selector: 'dynamic-component',
template: `<h2>I'm dynamically attached</h2>`
})
export class DynamicComponent { }

2. Service Loader

Then, we create a service to create the component:

import {
ComponentFactoryResolver,
Injectable,
Inject,
ReflectiveInjector
} from '@angular/core'
import { DynamicComponent } from './dynamic.component'
@Injectable()
export class Service {
  constructor(@Inject(ComponentFactoryResolver) factoryResolver) {
this.factoryResolver = factoryResolver
}
  setRootViewContainerRef(viewContainerRef) {
this.rootViewContainer = viewContainerRef
}
  addDynamicComponent() {
const factory = this.factoryResolver
.resolveComponentFactory(DynamicComponent)
const component = factory
.create(this.rootViewContainer.parentInjector)
this.rootViewContainer.insert(component.hostView)
}
}

The Service must have an exposed method to set the ViewContainerRef ’cause it’s not possible to inject the ViewContainerRef by using the service’s constructor due to it not being a Component, so we must use a setter method: setRootViewContainer.

addDynamicContainer adds the DynamicComponent to the DOM.

3. Main Component

The main component will inject it’s container (ViewContainerRef ) to the service:

import { 
Component,
Inject,
ViewContainerRef
} from '@angular/core'
import { Service } from './service'
@Component({
selector: 'my-app',
template: `<h1>Hello {{name}}</h1>`
})
export class AppComponent {
  name = 'from Angular'
  constructor(@Inject(Service) service, 
@Inject(ViewContainerRef) viewContainerRef) {
service.setRootViewContainerRef(viewContainerRef)
service.addDynamicComponent()

}
}

4. Main Module: entryComponents

This is where most documentation lacks on instructing to add the dynamic component as an entryComponent, at least in the documentation I was able to find.

According to the NgModule definition, entryComponents:

Specifies a list of components that should be compiled when this module is defined. For each component listed here, Angular will create a ComponentFactory and store it in the ComponentFactoryResolver.
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { AppComponent } from './app.component'
import { Service } from './service'
import { DynamicComponent } from './dynamic.component'
@NgModule({
imports: [BrowserModule],
declarations: [
AppComponent,
DynamicComponent
],
providers: [Service],
bootstrap: [AppComponent],
entryComponents: [DynamicComponent]
})
export class AppModule { }

The entryComponents will create a factory so that when the ComponentFactoryResolver is called we are able to create an instance of the component and add it to the DOM.

Use Cases

So, you might be wondering why you’d want to create a component from a service, don’t you? Well, you might have a process that’s running in the background, like an interval or a timeout, that needs to show a component (like an alert/notification or a modal) when certain conditions are given, and actually placing these components in a template and displaying them (or not) with a flag might bring along some other concerns like: Code duplicates, High Coupling, and your component will do things out of its purpose which implies more code (error prone).

Two main libraries in the ng2 community work this way, one for alerts/notifications (Toastr) and another one to show modals (ng2-bootstrap-modal). This kind of component needs to be created dynamically, ’cause otherwise you’ll end up bloating up your templates with code duplicates to display an alert or a modal.

Dynamic Children

To add a component as a children instead as a sibling the changes are but a few. (see the Plunker)

We have two components, a container (my-app) and the dynamic presenter (dynamic-component). We just need to modify the container, the presenter remains the same.

What we have to do is use the ng-template tag (just template in Angular2) and grab it from the container by using @ViewChild. We also need to move the service calls from the constructor to the OnInit method. I’ve highlighted the changes.

import { 
Component,
NgModule,
OnInit,
ViewChild,

ViewContainerRef
} from '@angular/core'
import { Service } from './service'
@Component({
selector: 'my-app',
template: `
<h1>Hello {{name}}</h1>
<ng-template #dynamic></ng-template>
`
})
export class AppComponent implements OnInit {
name = 'from Angular'
  @ViewChild('dynamic', { 
read: ViewContainerRef
}) viewContainerRef: ViewContainerRef
  constructor(@Inject(Service) service) {
this.service = service
}
  ngOnInit() {
this.service.setRootViewContainerRef(this.viewContainerRef)
this.
service.addDynamicComponent()
}
}

Now, the viewContainerRef is going to reference the ng-template tag, and it’s gonna substitute it for the dynamic component.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.