August 17, 2016
Dynamically Render Components with ComponentFactoryResolver in Angular 2
In my current project I came to a situation where it was necessary to drop specific components into my view. Since the project is a game and the current view should not be tied to URLs at this point i decided not to use the router but to load my various components dynamically into my view. This gives me the power to handle the logic in one persistent smart component which is bound to a URL.
My project was build with Ionic 2 and Angular, but i’ll go with plain Anuglar 2 for this blog post. The equivalent approach in Angular 1 would have used the $compile service. In Angular 2 there was a very simple approach using the ComponentResolver but the class is deprecated and will be removed from Angular 2. This blog post will guide you how to use the ComponentFactoryResolver together with NgModule in order to render Angular 2 components dynamically. This approach is only eligible for components that already exist in your application and not for components that will be received trough API calls for example. To live render external compontents take a look in this Stackoverflow question.
First of all lets create a fresh Angular 2 RC5 project. Make sure to have the angular-cli webpack beta installed to create a new project with RC5.
1 2 3 4 5 6 7 8 9 |
# Remove old versions npm uninstall -g angular-cli # Install the new version npm install --global angular-cli@1.0.0-beta.11-webpack.2 # Verify everything went good by ng version # -> should return: angular-cli: 1.0.0-beta.11-webpack.2 |
Then create the new project:
1 |
ng new component-example |
And launch the project with:
1 2 |
cd component-example ng serve |
Now you are all set, lets get started with the actual code! This empty setup provides us with an AppComponent that will act as our host component in which we want to render several child components. So lets create the child components first.
1 2 |
ng generate component child ng generate component another-child |
Instead of calling the components in our HTML code we want to inject them via JavaScript when needed. Since we have no logic lets just go with a setTimeout as initiator of the component rendering. Add this dummy code to your app components constructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.css'] }) export class AppComponent { title = 'app works!'; constructor () { setTimeout(()=>{ // at this point we want the "child" component to be rendered into the app.component: setTimeout(()=>{ // at this point we want the "another-child" component to be rendered into the app.component: }, 1000); }, 1000); } } |
Now add a DIV to the AppComponent which will hold our child components:
1 2 3 4 5 |
<h1> {{title}} </h1> <!--div #parent will contain our child components --> <div #parent></div> |
Capture the #parent element in your TypeScript code as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import {Component, ViewChild, ViewContainerRef} from '@angular/core'; @Component({ selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.css'] }) export class AppComponent { title = 'app works!'; @ViewChild('parent', {read: ViewContainerRef}) parent: ViewContainerRef; constructor () { setTimeout(()=>{ // at this point we want the "child" component to be rendered into the app.component: setTimeout(()=>{ // at this point we want the "another-child" component to be rendered into the app.component: }, 1000); }, 1000); } } |
To get the components we use the ComponentFactoryResolver, inject it into the constructor and declare 2 variables which will receive our components:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import {Component, ViewChild, ViewContainerRef, ComponentFactoryResolver} from '@angular/core'; import {ChildComponent} from "./child/child.component"; import {AnotherChildComponent} from "./another-child/another-child.component"; @Component({ selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.css'] }) export class AppComponent { title = 'app works!'; @ViewChild('parent', {read: ViewContainerRef}) parent: ViewContainerRef; constructor (private componentFactoryResolver: ComponentFactoryResolver) { const childComponent = this.componentFactoryResolver.resolveComponentFactory(ChildComponent); const anotherChildComponent = this.componentFactoryResolver.resolveComponentFactory(AnotherChildComponent) setTimeout(()=>{ // at this point we want the "child" component to be rendered into the app.component: setTimeout(()=>{ // at this point we want the "another-child" component to be rendered into the app.component: }, 1000); }, 1000); } } |
Now we are ready to inject the childComponent and the anotherChildComponent into our view. To do so call the createComponent method on the parent object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import {Component, ViewChild, ViewContainerRef, ComponentFactoryResolver} from '@angular/core'; import {ChildComponent} from "./child/child.component"; import {AnotherChildComponent} from "./another-child/another-child.component"; @Component({ selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.css'] }) export class AppComponent { title = 'app works!'; @ViewChild('parent', {read: ViewContainerRef}) parent: ViewContainerRef; constructor (private componentFactoryResolver: ComponentFactoryResolver) { const childComponent = this.componentFactoryResolver.resolveComponentFactory(ChildComponent); const anotherChildComponent = this.componentFactoryResolver.resolveComponentFactory(AnotherChildComponent) setTimeout(()=>{ // at this point we want the "child" component to be rendered into the app.component: this.parent.createComponent(childComponent); setTimeout(()=>{ // at this point we want the "another-child" component to be rendered into the app.component: this.parent.createComponent(anotherChildComponent); }, 1000); }, 1000); } } |
The last required step is to list the components as entryComponents either in your app.components.ts @Component({}) decorator or in the app.modules.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import { BrowserModule } from '@angular/platform-browser'; import { NgModule, ApplicationRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { ChildComponent } from './child/child.component'; import { AnotherChildComponent } from './another-child/another-child.component'; @NgModule({ declarations: [ AppComponent, ChildComponent, AnotherChildComponent ], imports: [ BrowserModule, CommonModule, FormsModule ], providers: [], entryComponents: [AppComponent, ChildComponent, AnotherChildComponent], bootstrap: [AppComponent] }) export class AppModule { } |
This should lead to the following result:
In a real world application you probably want to remove the dynamically rendered components at some point. In order to do so it is required to store the return of this.parent.createComponent()
in a variable. Lets add another setTimeout and remove the another child component after another 500 milliseconds.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import {Component, ViewChild, ViewContainerRef, ComponentFactoryResolver} from '@angular/core'; import {ChildComponent} from "./child/child.component"; import {AnotherChildComponent} from "./another-child/another-child.component"; @Component({ selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.css'] }) export class AppComponent { title = 'app works!'; @ViewChild('parent', {read: ViewContainerRef}) parent: ViewContainerRef; constructor(private componentFactoryResolver: ComponentFactoryResolver) { const childComponent = this.componentFactoryResolver.resolveComponentFactory(ChildComponent); const anotherChildComponent = this.componentFactoryResolver.resolveComponentFactory(AnotherChildComponent) let anotherChildComponentHolder; setTimeout(()=> { // at this point we want the "child" component to be rendered into the app.component: this.parent.createComponent(childComponent); setTimeout(()=> { // at this point we want the "another-child" component to be rendered into the app.component: anotherChildComponentHolder = this.parent.createComponent(anotherChildComponent); setTimeout(()=> anotherChildComponentHolder.destroy(), 500) }, 1000); }, 1000); } } |
Thats it! If you have any questions feel free to use the comment section.
Thanks for the example, it saved me a lot of investigation time! Do you know if the same approach is also compatible with Angular 2 RC6?
Would you be able to provide final release Example? Trying to find a way to load existing components dynamically,, but I cant find.
Hello Abner,
the full code is above. Just kickstart a new ng2 project with angular cli and then paste in the snippets from above.
If you receive any error, I’m happy to help.
I moved my component creation to service and couldn’t move forward. I didn’t know that
entryComponents
exists not only in@Component
but also in@NgModule
. Thanks!Hello,
can this be applied to content gotten from an API call?
I am getting for example this
this.pageContent = ”
Some title
Lorem ipsum text”
right now I am doing in my view:
Obviously “page-component-test” component is not getting transcluded
How do I do that on the fly? (or after I get the response from the API)
I have found this solution to load component into DOM element without using ViewContainer:
import {Component, ComponentFactoryResolver, Injector, ApplicationRef} from ‘@angular/core’;
import {ChildComponent} from “./child/child.component”;
@Component({
selector: ‘app-root’,
template: ”
})
export class AppComponent {
constructor (private componentFactoryResolver: ComponentFactoryResolver, private injector: Injector, private appRef: ApplicationRef) {
}
createChildComponent() {
const childComponent = this.componentFactoryResolver.resolveComponentFactory(ChildComponent);
var childComponentRef = childComponent.create(this.injector, null, ‘#parent’);
(this.appRef)._loadComponent(childComponentRef);
}
}
‘_loadComponent’ does not exist on type ‘ApplicationRef’
Hi Prateek ,
Are you able to get any solution for transcluding the child selectors that are in a string and display them on view? Thanks!!
eh, the HTML is not showing in the comment
https://jsfiddle.net/pbre3rbk/ <- here
Hey Damir ,
Are you able to figure out any solution for this ? I’m facing the same issue and need some solution for it.Thanks!!
Excellent article, thank you for documenting – I have one question, how would you go about passing inputs into the child components?
@Jay: Working from the last example above where you store the return of the CreateComponent method, you can access properties on the child component via the instance property: anotherChildComponentHolder.instance.myProp = “myVal”; You can also call functions on the created component the same way: anotherChildComponentHolder.instance.myFunc(“hello world”);
HTH.
Thanks for taking the time to write this out, it helped a lot!
Is there any way to render components which be received from server?
Probably, take a look at Angular Universal, Ahead of Time Compiling and Lazyloading. But in general you should send data from your server – not components.
Is it possible to pass some data like object or array to child component?
Yes for sure.
When you create a component, you get the ComponentRef in return. Calling the “instance” gives you full access to the class of ChildComponent
this.parent.createComponent(childComponent).instance.someMethodOnChildComponent
Hi,
Is it a safe solution ? Is it possible that someone replace the component code loaded dynamically ?
Best regards
Yes this should be safe. Since this approach doesn’t dynamically loads the component. This would be bad practice (at least in my opinion). You should load dynamic data not dynamic templates. The component is served with the client. Also it is listed in the entryComponents. So Ahead of Time Compiling should work out of the box.
Thank you. You are right, it is not loaded dynamical, it is render dynamical. Thank you for sharing this solution.
Thank you for your effort.
zero help from angular 2 guys, i why need to import anotherchildcomponent() if it is dynamic image using it in an enterprise application where you have thousends of components that load on demand.
from this example i can tell i will write them all inside entrypointcomponent and also have to import thousands of components
Have you considered lazy loading?
when inspecting the components, it appears to load them below the div, not within
I’d recommend you to take a look at NgComponentOutlet. Its a new feature of Angular 4.
Thanks alot!
really clear and helpful !
Why is the code just running inside the setTimeout() functions? If I try to run this.parent.createComponent() directly under const anotherChildComponent = this.componentFactoryResover.resolveComponentFactory() I get an error “error_handler.js:54 EXCEPTION: Cannot read property ‘createComponent’ of undefined”. How can I run the tasks without timeout functions?
Because the parent component (div#parent) is not yet loaded. This was just an example, if you want to do it on Component initialization then the constructor is the wrong place.
Move your code to the OnInit Lifecycle Hook and it will work.
Can we pass parameters to child component?
I am not a fan of the setTimeout(), maybe move the code to ngOnInit()
Just curious….how would we add …. to . I can see in my inspector it adds ….
in the tbody as wanted but the display is incorrect. If i edit the html in the inspect and remove it works great.
How can I correctly insert the …. in my child element in the tbody of the parent component?
Hi,
Thanks very much. It works well. Just one question: In my child component, I have a text box and a button. On click of the button, I want to send the data entered in the texbox and “emit” it back to the parent. For that I tried:
parent:
global_data:any[]=[];
let instance:any=this.stockin_items_containair.createComponent(this.stockItemFormComponent);
instance.instance.eventActionHandler.subscribe(this.emittedNewRowAction)
emittedNewRowAction($event){
//$event contains data sent from child so save it now
console.log($event); // ===================>CORRECT. I see data emitted
this.global_data.push({‘id’:$event}); ==> tihs.global_data is undefined.
}
child:
@Output() eventActionHandler=new EventEmitter();//
sendValueToParent(){
this.eventActionHandler.emit(data);
}
How to render child components withoutusing setTimeout function ? May be on a button click
Might be good to note an update to the @ViewChild decorator, as after Angular version 8 (optional?) or 9 (required?) there is an additional attribute “static” that needs to be added and set to true or false:
@ViewChild('parent', { read: ViewContainerRef, static: false })
See https://angular.io/guide/static-query-migration
Hello, always i used to check web site posts here early in the dawn, as i love to gain knowledge of more and more.
Thanks a lot! But what if I want to render also the sub-components of a dynamic component?
Hi,
My child element have a property that I’d like to pass when I create it.
I’ve tried multiple things, it didn’t work.
Can you tell me what I should add here ?
Cellcomponent is the child and I would like to add an id inside.
const childComponent = this.componentFactoryResolver.resolveComponentFactory(CellComponent);
// here should be added the id
this.parent.createComponent(childComponent);