2017-03-01 51 views
11

我假设在Angular-cli tree-shaking exclude component from removal的问题是非常相似,但我似乎无法得到任何东西。Angular2(CLI)树抖动删除动态创建的NgModule

本质上,我有一个动态元件工厂,如How can I use/create dynamic template to compile dynamic Component with Angular 2.0?中所述。

当我使用最新的非生产设置的Angular CLI构建它时,它一切正常。但是,一旦我使用生产环境我立即得到在浏览器下面的错误跟踪尝试加载已动态创建内容的页面时:

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)

这里是我的组件工厂类:

@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; 
    } 
} 

正被transpiled到

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 
}() 

在错误消息中的“E”是函数e()createComponentModule WHI ch,正如你所看到的,尽管应该包含@NgModule内容,但它仍然是空的。

如何动态创建新的NgModule并仍然使用Angular CLI的生产模式?

版本:
Angular2:2.4.8
角CLI:1.0.0-beta.32.3
打字稿:2.1.6

回答

0

我有相同的错误消息。我发现的解决方法是不使用运行时模块的装饰器。

protected createComponentModule(componentType: any, additionalModules: any[]): Type<any> { 
    return NgModule({ 
    imports: [ 
     FormsModule, 
     ReactiveFormsModule, 
     BrowserModule, 
     PipesModule, 
     ...additionalModules 
    ], 
    declarations: [ 
     componentType 
    ], 
    schemas:[CUSTOM_ELEMENTS_SCHEMA] 
    })(class RuntimeComponentModule {}); 
} 

好吧,我并不完全明白为什么会出现错误。错误消息基本上说,模块e没有元数据。 Angular中模块的元数据通常被声明为装饰器。

ES7中的装饰器等同于咖喱功能。这意味着

@NgModule({}) 
class A {} 

等于

NgModule({})(class A {}) 

我个人认为,咖喱方式要好得多......

更新22比赛: 从官方回购答案https://github.com/angular/angular-cli/issues/5359#issuecomment-287500703 是根本不使用AOT。 请用ng build --prod --no-aot 构建代码在我的情况下,一切都解决了。

+0

恐怕它不能正常工作。代码编译得很好,并且包含在经过转换的JS中,但是,现在有一个新问题。 Angular在新的'NgModule'中找不到任何自定义导入的模块。所以在上面的例子'FormsModule'中,可以找到'ReactiveFormsModule'和'BrowserModule',但是'PipesModule'不能。错误在'CompileMetadataResolver.getNgModuleMetadata'中。 – Sebastian

+0

其实我有同样的问题。这似乎在JIT编译根本不编译装饰器。不仅是'RuntimeComponentModule'。如果你读过编译过的'main.js',它不包含'PipesModule'的元数据。我会继续这个tmr。 Thx告诉我角模块很好。 –

+0

它现在正在工作jit + aot。我正在使用它在v4中使用 –

0

不幸的是,现在看来,这似乎是不可能的(我会尽力让答案保持最新),Angular 2.x和Angular 4 beta都不会。
问题在于,动态组件定义中包含的文件引用(模板,样式表)在运行时无法再用之前运行的AOT编译器解析。
但是,如果组件或模块不包含文件引用,当前的Angular代码不允许真正动态创建组件。它只是找不到在运行时创建的元数据。

总结问题,有3个级别的动态组件创建的:

  1. 静态定义的组件,它包括在NgModule的AOT编译器可以在AOT找到编译时间。这样的组件可以在任何时候实例化而没有问题。 (请参阅ComponentFactoryResolver等)
  2. 静态定义组件的主体(代码等),但允许具有动态模板和/或样式(即在需要时在代码中创建模板)。这也需要在运行时编译一个NgModule。目前只有在不使用AOT编译器并且代表我在此处发布的问题时才有可能。
  3. 动态定义完整的组件,包括代码和模板。这不是这里的目的,甚至可能会走得很远。但是可能有人也有这个问题。

在我看来,2号问题可以解决。 Angular团队说因为它是AOT,它只能编译AOT编译时静态已知的那些东西,但我不同意。
我可以想到AOT编译这个组件的“存根”的可能性,然后在需要时使用动态模板或样式表进行实例化。可能需要使用@Component注释的新属性或像@DynamicComponent这样的全新注释,但对我来说似乎是可行的。我不知道@NgModule声明是否需要进行相同的更改,但我认为他们会这么做。

+0

。 Jit + Aot一次:) –

+0

是的,在github上看到了你的线程!现在我们需要让它在CLI中工作。 – Sebastian

+0

ciao塞巴斯蒂安! –