**This post currently reflects an earlier version of Angular, and may not be up to date or reflect the current release. To learn more about about the latest version of Angular and migration, follow the link here **
The Evolution
Creating components has been a topic of interest at Rangle because with each RC version of Angular 2, it seemed like the way to do so had changed.
Near the beginning there was the DynamicComponentLoader.
After that was deprecated, there was ComponentResolver.
And now that the dust has settled, there is the ComponentFactoryResolver!
The (Main) Reason
Why would I ever want to dynamically create a component? Modals
- Imagine throughout your app you have buttons that trigger different modals to open
- Some of these modals need to be initialized with some information for their state
- And as we know, modals are best kept as children of the body tag to avoid issues
A good solution to this problem would be a modal factory.
The modal factory would:
- Be given the component class, representing the modal we want, along with any information it needs
- Use this information and create the modal on the fly
- Keep reference to the created component and destroy it at some point
The Code / Explanation
Imagine we have a simple component called App that contains two buttons:
- One to create a hello world modal component
- One to create a world hello modal component
In App we have another component with the selector dynamic-component. This component takes a property called componentData which gives reference to the component we want to create and any information we want to pass down.
It could look something like this:
@Component({
selector: 'my-app',
template: `
<div>
<h2>Lets dynamically create some components!</h2>
<button (click)="createHelloWorldComponent()">Create Hello World</button>
<button (click)="createWorldHelloComponent()">Create World Hello</button>
</div>
<dynamic-component [componentData]="componentData"></dynamic-component>
`,
})
export class App {
componentData = null;
createHelloWorldComponent(){
this.componentData = {
component: HelloWorldComponent,
inputs: {
showNum: 9
}
};
}
createWorldHelloComponent(){
this.componentData = {
component: WorldHelloComponent,
inputs: {
showNum: 2
}
};
}
}
By clicking on either of the Create Hello World or Create World Hello buttons, the appropriate event will fire, sending data into dynamic-component.
The dynamic-component will now generate the appropriate component using the information given.
Now let's look at how this DynamicComponent would be implemented.
This component will need a few things:
- Knowledge of all possible components it will be creating
- A place to put a created component
- Access to the ComponentFactoryResolver service
- A variable to store a created component so we can remove it later
import {Component, ViewContainerRef, ViewChild, ReflectiveInjector, ComponentFactoryResolver} from '@angular/core';
import HelloWorldComponent from './hello-world-component';
import WorldHelloComponent from './world-hello-component';
@Component({
selector: 'dynamic-component',
entryComponents: [HelloWorldComponent, WorldHelloComponent], // Reference to the components must be here in order to dynamically create them
template: `
<div #dynamicComponentContainer></div>
`,
})
export default class DynamicComponent {
currentComponent = null;
@ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) {
}
…
}
As you can see above, entryComponents is where we inform DynamicComponent of the possible components we want it to create.
We’ve attached a reference to a div in our class so that we can access it and place the component we create inside of it.
We’ve injected the ComponentFactoryResolver into the class.
And lastly, we've created a variable called currentComponent to store the created component.
With all of the setup complete, we’re able to move on to the actual component creation!
Let's implement an Input setter that takes a component class and an object with key/value pairs mapping to what we want access in the created component.
// component: Class for the component you want to create
// inputs: An object with key/value pairs mapped to input name/input value
@Input() set componentData(data: {component: any, inputs: any }) {
if (!data) {
return;
}
// Inputs need to be in the following format to be resolved properly
let inputProviders = Object.keys(data.inputs).map((inputName) => {return {provide: inputName, useValue: data.inputs[inputName]};});
let resolvedInputs = ReflectiveInjector.resolve(inputProviders);
// We create an injector out of the data we want to pass down and this components injector
let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicComponentContainer.parentInjector);
// We create a factory out of the component we want to create
let factory = this.resolver.resolveComponentFactory(data.component);
// We create the component using the factory and the injector
let component = factory.create(injector);
// We insert the component into the dom container
this.dynamicComponentContainer.insert(component.hostView);
// Destroy the previously created component
if (this.currentComponent) {
this.currentComponent.destroy();
}
this.currentComponent = component;
}
The function above does the following:
- Turns the inputs into a format Angular 2 can understand
- Creates an injector out of the resolved inputs and DynamicComponent injector
- Creates a factory out of the component we want to create
- Creates a component out of the factory and injector
- Inserts the component into the placeholder container we created earlier
- Destroys the previously created component
And there you have it, we’ve dynamically created a modal!
The Example
Here's a simple Plunker I made demonstrating what was explained above: http://plnkr.co/edit/ZXsIWykqKZi5r75VMtw2?p=preview
More Resources
- View and download our extensive Angular 2 course book, created in-house by Rangle’s team of JavaScript experts. The training book covers fundamental topics including how to get started with the Angular 2 toolchain.
- Sign up for Rangle's Angular 2 Online Training, free of cost. Whether you're new to Angular 2 or an experienced developer, our two-day course will get you up to speed.