I assume the question at Angular-cli tree-shaking exclude component from removal is very similar but I can't seem to get anything out of it.
Essentially I have a dynamic component factory as described in How can I use/create dynamic template to compile dynamic Component with Angular 2.0?.
When I build it using the latest Angular CLI with a non-production setting, it all works fine. However, once I use the production setting I get the following error trace in the browser immediately when trying to load a page that has dynamically created content:
EXCEPTION: No NgModule metadata found for 'e'.
ORIGINAL STACKTRACE:
main.dc05ae9….bundle.js:formatted:4731
Error: No NgModule metadata found for 'e'.
at f (vendor.c18e6df….bundle.js:formatted:76051)
at t.resolve (vendor.c18e6df….bundle.js:formatted:20624)
at t.getNgModuleMetadata (vendor.c18e6df….bundle.js:formatted:20169)
at t._loadModules (vendor.c18e6df….bundle.js:formatted:40474)
at t._compileModuleAndAllComponents (vendor.c18e6df….bundle.js:formatted:40462)
at t.compileModuleAndAllComponentsSync (vendor.c18e6df….bundle.js:formatted:40436)
at e.createComponentFactory (main.dc05ae9….bundle.js:formatted:4789)
Here is my component factory class:
@Injectable() export class DynamicTypeBuilder { constructor() { } private _cacheOfFactories: {[templateKey: string]: ComponentFactory<any>} = {}; private compiler: Compiler = new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler(); public createComponentFactory<COMPONENT_TYPE>(type: any, template: string, additionalModules: any[] = []): Observable<ComponentFactory<COMPONENT_TYPE>> { let factory = this._cacheOfFactories[template]; if (factory) { return Observable.of(factory); } // unknown template ... let's create a Type for it let module = this.createComponentModule(type, additionalModules); // compiles and adds the created factory to the cache return Observable.of(this.compiler.compileModuleAndAllComponentsSync(module)) .map((moduleWithFactories: ModuleWithComponentFactories<COMPONENT_TYPE>) => { factory = moduleWithFactories.componentFactories.find(value => value.componentType == type); this._cacheOfFactories[template] = factory; return factory; }); } protected createComponentModule(componentType: any, additionalModules: any[]): Type<any> { @NgModule({ imports: [ FormsModule, ReactiveFormsModule, BrowserModule, PipesModule, ...additionalModules ], declarations: [ componentType ], schemas:[CUSTOM_ELEMENTS_SCHEMA] }) class RuntimeComponentModule { } return RuntimeComponentModule; } }
which is being transpiled to
var _ = function() { function e() { this._cacheOfFactories = {}, this.compiler = new i.a([{ useDebug: !1, useJit: !0 }]).createCompiler() } return e.prototype.createComponentFactory = function(e, t, n) { var i = this; var _ = this._cacheOfFactories[t]; if (_) r.Observable.of(_); var a = this.createComponentModule(e, n); return r.Observable.of(this.compiler.compileModuleAndAllComponentsSync(a)).map(function(n) { return _ = n.componentFactories.find(function(t) { return t.componentType == e }), i._cacheOfFactories[t] = _, _ }) } , e.prototype.createComponentModule = function(e, t) { var n = function() { function e() {} return e }(); return n } , e.ctorParameters = function() { return [] } , e }()
The 'e' in the error message is the function e()
from createComponentModule
which, as you can see, is empty even though is should contain the @NgModule
content.
How can I create a new NgModule dynamically and still use the production mode of Angular CLI?
Versions:
Angular2: 2.4.8
Angular CLI: 1.0.0-beta.32.3
TypeScript: 2.1.6
Unfortunately it really seems like this is impossible at the moment (I will try to keep the answer up to date), neither with Angular 2.x nor with Angular 4 beta.
The problem being that a dynamic component definition contains file references (template, style sheets) which cannot be resolved any more at run time with the AOT compiler having run before.
But also if the component or module would not contain file references the current Angular code does not allow for a truly dynamic creation of components. It just doesn't find the metadata that is being created at runtime.
Summarizing the problem, there are 3 levels of dynamic component creation:
In my opinion the number 2 issue can be solved. The Angular team says since it is AOT it can only compile those things that are statically known at AOT compile time, but I disagree.
I could think of the possibility to AOT compile a 'stub' of such a component which is then being instantiated with a dynamic template or style sheet when needed. There might be the need to use a new property for the @Component
annotation or a totally new annotation like @DynamicComponent
but it seems feasible to me. I do not know if the same changes would be required for @NgModule
declaration, but I assume they would.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With