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塞巴斯蒂安! –