This post explains how to create component in runtime. I will show two scenario:
1. How to inject a component in runtime without knowing which component will inject until runtime.
2. How to inject HTML with components in runtime. The HTML will creates in runtime by some logics.
The code is based on Angular 2 RC 6.
Click here for live example.
Demo 1 – Inject component in runtime
The goal:
Injecting components in runtime that have not been loaded or compiled before on the client side.
When we click on the button “Dynamic component (Start)” the application starts add string to the shapes array every second. The string is random form names of shapes (circle, triangle, square and rectangle). Each string converts to component. Only when we click on button the components load and compiles.
Code explanations:
Only the shape-dynamic-loader component (shape.component.ts) needs to be explained, all the others are pretty obvious.
1. The template of this component is very simple; Placeholder for the shape’s component (circle, square, triangle and rectangle).
<span #span>span>
2. I use @ViewChild to get a ViewContainerRef of the span element. I will use it for creating the right shape component.
@ViewChild('span', { read: ViewContainerRef }) span;
3. Every time I set the source input, the createComponent method is executed.
_shape:string;
@Input('source')
set shape(value){
this._shape = value;
this.createComponent();
}
4. The createComponent method does all the hard work.
createComponent(){
let injector = ReflectiveInjector
.fromResolvedProviders([], this.vcRef.parentInjector);
// 1. Create module loader
let loader = new SystemJsNgModuleLoader(this.compiler);
loader.load('app/shapes/shapes.module')
.then((nmf:NgModuleFactory<any>)=>{
// 2. create NgModuleRef
let ngmRef = nmf.create(injector);
let shape = ngmRef.instance.shapes.get(this._shape);
// 3. Create component factory
let cmpFactory = ngmRef
.componentFactoryResolver
.resolveComponentFactory( shape );
// 4. Create the component
let componentRef = this.span.createComponent(cmpFactory,0,injector,[]);
// 5. Init the component name field.
componentRef.instance.name = this._shape;
// 6. Refresh the component area.
componentRef.changeDetectorRef.detectChanges();
componentRef.onDestroy(()=> {
componentRef.changeDetectorRef.detach();
});
});
}
Demo 2 – Inject HTML with components in runtime
The goal:
Injecting in runtime HTML with components that have not been loaded or compiled before on the client side.
When we click on Dynamic HTML button the application will create HTML string and compile it and add it to the DOM.
Code explanations:
This demo is more complicated because we need to create a HTML on the fly. We need to load the components that exist in the HTML string that we created in runtime.
1. Create module loader same as the first demo. createDynamicComponentFromHtml method.
2. Load the modules that have the components that we need, for the HTML. Again, same as first demo.
3. Create component in runtime that has the HTML.
As you can see, this is a simple template. Usually we create here dynamic template base on some logics.
4. Create a module that imports all the modules I need for the template. After the module is created, I have to compile it.
createDynamicFactory(injector,moduleClass){
@Component({
selector : 'dynamic-html',
template : `…`})
class DynamicHtmlComponent{}
@NgModule({
imports : [moduleClass],
declarations : [DynamicHtmlComponent],
entryComponents: [DynamicHtmlComponent]
})
class DynamicModule{}
return this.compiler
.compileModuleAsync(DynamicModule)
.then((ngMdlFactory)=>{
let ngMdlRef = ngMdlFactory.create(injector);
// Create component factory
let cmpFactory = ngMdlRef.componentFactoryResolver
.resolveComponentFactory( DynamicHtmlComponent );
return cmpFactory;
});
}
This method does 3 imported steps:
a. Compile the module and return NgModuleFactory.
b. Create NgModuleRef from NgModuleFactory.
c. Create ComponentFactory from NgModuleRef.
5. Now that I have ComponentFactory the rest of the code is same as first demo.
this.createDynamicFactory(injector,ngMdlRef.instance.constructor)
.then((cmpF)=>{
// Create the component
let componentRef = this.span.createComponent(cmpF,0,injector,[]);
componentRef.changeDetectorRef.detectChanges();
componentRef.onDestroy(()=> {
componentRef.changeDetectorRef.detach();
});
});
Summary
First demo shows you how we can create a component in runtime. This scenario is good when you don’t know in advance which component you need to use. In our example the list is not homogeneous, I don’t know the shapes until runtime. After the module loaded to the client it will not load again.
Second demo shows you how to create HTML in runtime with components; compile it and inject it.
The demos shows you in action the classes:
1. SystemJsNgModuleLoader
2. Compiler
3. NgModuleFactory
4. NgModuleRef
5. ComponentFactory
6. ViewContainerRef
7. ChangeDetectorRef
I hope this information is helpful for you; send feedback please :=).
The next course opens on 18 September. For more information click here.