I want to dynamically create a template. This should be used to build a ComponentType
at runtime and place (even replace) it somewhere inside of the ho
I have a simple example to show how to do angular 2 rc6 dynamic component.
Say, you have a dynamic html template = template1 and want to dynamic load, firstly wrap into component
@Component({template: template1})
class DynamicComponent {}
here template1 as html, may be contains ng2 component
From rc6, have to have @NgModule wrap this component. @NgModule, just like module in anglarJS 1, it decouple different part of ng2 application, so:
@Component({
template: template1,
})
class DynamicComponent {
}
@NgModule({
imports: [BrowserModule,RouterModule],
declarations: [DynamicComponent]
})
class DynamicModule { }
(Here import RouterModule as in my example there is some route components in my html as you can see later on)
Now you can compile DynamicModule as:
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule).then(
factory => factory.componentFactories.find(x => x.componentType === DynamicComponent))
And we need put above in app.moudule.ts to load it, please see my app.moudle.ts. For more and full details check: https://github.com/Longfld/DynamicalRouter/blob/master/app/MyRouterLink.ts and app.moudle.ts
and see demo: http://plnkr.co/edit/1fdAYP5PAbiHdJfTKgWo?p=preview
I myself am trying to see how can I update RC4 to RC5 and thus I stumbled upon this entry and new approach to dynamic component creation still holds a bit of mystery to me, so I wont suggest anything on component factory resolver.
But, what I can suggest is a bit clearer approach to component creation on this scenario - just use switch in template that would create string editor or text editor according to some condition, like this:
<form [ngSwitch]="useTextarea">
<string-editor *ngSwitchCase="false" propertyName="'code'"
[entity]="entity"></string-editor>
<text-editor *ngSwitchCase="true" propertyName="'code'"
[entity]="entity"></text-editor>
</form>
And by the way, "[" in [prop] expression have a meaning, this indicates one way data binding, hence you can and even should omit those in case if you know that you do not need to bind property to variable.
If all you need as a way to parse a dynamic string and load components by their selectors, you may also find the ngx-dynamic-hooks library useful. I initially created this as part of a personal project but didn't see anything like it around, so I polished it up a bit and made it public.
Some tidbids:
Notably, it does not rely on a runtime-compiler like some of the other responses here. Because of that, you can't use template syntax. On the flipside, this means it works in both JiT and AoT-modes as well as both Ivy and the old template engine, as well as being much more secure to use in general.
See it in action in this Stackblitz.
Solved this in Angular 2 Final version simply by using the dynamicComponent directive from ng-dynamic.
Usage:
<div *dynamicComponent="template; context: {text: text};"></div>
Where template is your dynamic template and context can be set to any dynamic datamodel that you want your template to bind to.
This is the example of dynamic Form controls generated from server.
https://stackblitz.com/edit/angular-t3mmg6
This example is dynamic Form controls is in add component (This is where you can get the Formcontrols from the server). If you see addcomponent method you can see the Forms Controls. In this example I am not using angular material,but It works (I am using @ work). This is target to angular 6, but works in all previous version.
Need to add JITComplierFactory for AngularVersion 5 and above.
Thanks
Vijay
I want to add a few details on top of this very excellent post by Radim.
I took this solution and worked on it for a bit and quickly ran into some limitations. I'll just outline those and then give the solution to that as well.
I made another question based on this post, on how to achieve these limitations, which can be found here:
recursive dynamic template compilation in angular2
I’ll just outline the answers to these limitations, should you run into the same issue as I, as that make the solution quite more flexible. It would be awesome to have the initial plunker updated with that as well.
To enable nesting dynamic-detail inside each other, You'll need to add DynamicModule.forRoot() in the import statement in the type.builder.ts
protected createComponentModule (componentType: any) {
@NgModule({
imports: [
PartsModule,
DynamicModule.forRoot() //this line here
],
declarations: [
componentType
],
})
class RuntimeComponentModule
{
}
// a module for just this Type
return RuntimeComponentModule;
}
Besides that it was not possible to use <dynamic-detail>
inside one of the parts being string-editor or text-editor.
To enable that you'll need to change parts.module.ts
and dynamic.module.ts
Inside parts.module.ts
You'll need to add DynamicDetail
in the DYNAMIC_DIRECTIVES
export const DYNAMIC_DIRECTIVES = [
forwardRef(() => StringEditor),
forwardRef(() => TextEditor),
DynamicDetail
];
Also in the dynamic.module.ts
you'd have to remove the dynamicDetail as they are now part of the parts
@NgModule({
imports: [ PartsModule ],
exports: [ PartsModule],
})
A working modified plunker can be found here: http://plnkr.co/edit/UYnQHF?p=preview (I didn’t solve this issue, I’m just the messenger :-D)
Finally it was not possible to use templateurls in the parts created on the dynamic components. A solution (or workaround. I’m not sure whether it’s an angular bug or wrong use of the framework) was to create a compiler in the constructor instead of injecting it.
private _compiler;
constructor(protected compiler: RuntimeCompiler) {
const compilerFactory : CompilerFactory =
platformBrowserDynamic().injector.get(CompilerFactory);
this._compiler = compilerFactory.createCompiler([]);
}
Then use the _compiler
to compile, then templateUrls are enabled as well.
return new Promise((resolve) => {
this._compiler
.compileModuleAndAllComponentsAsync(module)
.then((moduleWithFactories) =>
{
let _ = window["_"];
factory = _.find(moduleWithFactories.componentFactories, { componentType: type });
this._cacheOfFactories[template] = factory;
resolve(factory);
});
});
Hope this helps someone else!
Best regards Morten