crear - page title angular 4
Agitación de árbol Angular2(CLI) eliminando NgModule creado dinámicamente (2)
Supongo que la pregunta sobre la eliminación del componente excluir temblores de árbol Angular-cli es muy similar, pero parece que no puedo sacar nada de eso.
Básicamente, tengo una fábrica de componentes dinámicos como se describe en ¿Cómo puedo usar / crear una plantilla dinámica para compilar componentes dinámicos con Angular 2.0? .
Cuando lo construyo usando la última CLI angular con una configuración que no es de producción, todo funciona bien. Sin embargo, una vez que uso la configuración de producción, obtengo el siguiente rastro de error en el navegador inmediatamente cuando intento cargar una página que tiene contenido creado dinámicamente:
EXCEPCIÓN: No se encontraron metadatos NgModule para ''e''.
STACKTRACE ORIGINAL:
main.dc05ae9 ... .bundle.js: formateado: 4731
Error: No se encontraron metadatos NgModule para ''e''.
en f (vendor.c18e6df ... .bundle.js: formateado: 76051)
at t.resolve (vendor.c18e6df ... .bundle.js: formateado: 20624)
en t.getNgModuleMetadata (vendor.c18e6df ... .bundle.js: formateado: 20169)
en t._loadModules (vendor.c18e6df ... .bundle.js: formateado: 40474)
en t._compileModuleAndAllComponents (vendor.c18e6df ... .bundle.js: formateado: 40462)
en t.compileModuleAndAllComponentsSync (vendor.c18e6df ... .bundle.js: formateado: 40436)
en e.createComponentFactory (main.dc05ae9 ... .bundle.js: formateado: 4789)
Aquí está mi clase de fábrica de componentes:
@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;
}
}
que está siendo transpilado a
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
}()
La ''e'' en el mensaje de error es la función e()
de createComponentModule
que, como puede ver, está vacía aunque debe contener el contenido de @NgModule
.
¿Cómo puedo crear un nuevo NgModule dinámicamente y seguir usando el modo de producción de Angular CLI?
Versiones:
Angular2: 2.4.8
CLI angular: 1.0.0-beta.32.3
TypeScript: 2.1.6
Lamentablemente, parece que en este momento es imposible (intentaré mantener la respuesta actualizada), ni con Angular 2.x ni con Angular 4 beta.
El problema es que una definición de componente dinámico contiene referencias de archivo (plantilla, hojas de estilo) que no se pueden resolver más en tiempo de ejecución con el compilador AOT ejecutado anteriormente.
Pero también, si el componente o módulo no contiene referencias de archivos, el código angular actual no permite una creación verdaderamente dinámica de componentes. Simplemente no encuentra los metadatos que se crean en el tiempo de ejecución.
Resumiendo el problema, hay 3 niveles de creación dinámica de componentes:
- Defina un componente estáticamente e inclúyalo en un NgModule que el compilador AOT puede encontrar en el tiempo de compilación AOT. Tal componente puede en cualquier momento ser instanciado sin problemas. (Ver ComponentFactoryResolver, etc.)
- Defina el cuerpo de un componente estáticamente (código, etc.) pero permita tener plantillas y / o estilos dinámicos (es decir, la plantilla se está creando en código solo cuando sea necesario). Esto también requiere que se compile un NgModule en tiempo de ejecución. Esto actualmente solo es posible cuando no se utiliza el compilador AOT y representa el problema que publiqué aquí.
- Defina el componente completo de forma dinámica, incluido el código y las plantillas. Esto no es lo que se pretende aquí e incluso podría llegar muy lejos. Pero posiblemente alguien también tenga este problema.
En mi opinión, el problema del número 2 puede ser resuelto. El equipo de Angular dice que, dado que es AOT, solo puede compilar aquellas cosas que se conocen estáticamente en el tiempo de compilación AOT, pero no estoy de acuerdo.
Podría pensar en la posibilidad de que AOT compile un ''resguardo'' de dicho componente, que luego se instanciará con una plantilla dinámica o una hoja de estilo cuando sea necesario. Puede ser necesario utilizar una nueva propiedad para la anotación @Component
o una anotación totalmente nueva como @DynamicComponent
pero me parece factible. No sé si se requerirían los mismos cambios para la declaración @NgModule
, pero supongo que lo harían.
Tengo el mismo mensaje de error. La solución alternativa que encontré es no utilizar el decorador para el módulo de tiempo de ejecución.
protected createComponentModule(componentType: any, additionalModules: any[]): Type<any> {
return NgModule({
imports: [
FormsModule,
ReactiveFormsModule,
BrowserModule,
PipesModule,
...additionalModules
],
declarations: [
componentType
],
schemas:[CUSTOM_ELEMENTS_SCHEMA]
})(class RuntimeComponentModule {});
}
De acuerdo, no entendí completamente por qué ocurre el error. El mensaje de error básicamente dice que el módulo e
no tiene metadatos. Los metadatos de un módulo en Angular generalmente se declaran como decorador.
Los decoradores en ES7 son equivalentes a las funciones de curry. Significa
@NgModule({})
class A {}
es igual a
NgModule({})(class A {})
Personalmente, creo que la forma de curry es mucho mejor ...
Actualizado 22 Coincidencia: la respuesta del repo oficial https://github.com/angular/angular-cli/issues/5359#issuecomment-287500703 es simplemente no usar AOT. Construye el código con ng build --prod --no-aot
En mi caso, todo está resuelto.