25

我认为Angular-cli tree-shaking exclude component from removal的问题非常相似,但我似乎无法从中得到任何东西。

本质上,我有一个动态组件工厂,如如何使用/创建动态模板以使用 Angular 2.0 编译动态组件?.

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

例外:没有找到 'e' 的 NgModule 元数据。
原始堆栈跟踪:
main.dc05ae9….bundle.js:formatted:4731
错误:找不到“e”的 NgModule 元数据。
在 f (vendor.c18e6df….bundle.js:formatted:76051)
在 t.resolve (vendor.c18e6df….bundle.js:formatted:20624)
在 t.getNgModuleMetadata (vendor.c18e6df….bundle.js:formatted: 20169)
在 t._loadModules (vendor.c18e6df….bundle.js:formatted:40474)
在 t._compileModuleAndAllComponents (vendor.c18e6df….bundle.js:formatted:40462)
在 t.compileModuleAndAllComponentsSync (vendor.c18e6df….bundle。 js:formatted:40436)
在 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;
  }
}

正在被转换为

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即使它应该包含@NgModule内容。

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

版本:
Angular2:2.4.8
Angular CLI:1.0.0-beta.32.3
TypeScript:2.1.6

4

2 回答 2

1

不幸的是,目前这似乎是不可能的(我会尽量保持最新的答案),无论是 Angular 2.x 还是 Angular 4 beta。
问题是动态组件定义包含文件引用(模板、样式表),在运行时无法再通过之前运行的 AOT 编译器解决这些引用。
但是,如果组件或模块不包含文件引用,当前的 Angular 代码也不允许真正动态地创建组件。它只是找不到在运行时创建的元数据。

总结问题,动态组件创建有3个层次:

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

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

于 2017-03-16T10:02:02.917 回答
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 中的装饰器等价于 curry 函数。它的意思是

@NgModule({})
class A {}

等于

NgModule({})(class A {})

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

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

于 2017-03-09T10:52:03.303 回答