Angular2 (CLI) shake дерева, удаляющий динамически созданный NgModule

Я предполагаю, что вопрос в Angular -cli-shi исключить компонент из удаления очень похож, но я не могу ничего извлечь из него.

По существу у меня есть динамический компонент factory, как описано в Как я могу использовать/создавать динамический шаблон для компиляции динамического компонента с Angular 2.0?.

Когда я создаю его с использованием последней Angular CLI с непроизводственной настройкой, все работает отлично. Однако, как только я использую производственный параметр, я сразу же получаю следующую ошибку в браузере при попытке загрузить страницу с динамически созданным контентом:

ИСКЛЮЧЕНИЕ: Нет метаданных NgModule, найденных для 'e'.
ОРИГИНАЛЬНЫЙ СТЕКЛАТ:
main.dc05ae9....bundle.js: отформатирован: 4731 Ошибка
: нет метаданных NgModule найдено для 'e'.
    at f (vendor.c18e6df....bundle.js: отформатировано: 76051)
    в t.resolve(vendor.c18e6df....bundle.js: отформатировано: 20624)
    в t.getNgModuleMetadata(vendor.c18e6df....bundle.js: отформатировано: 20169)
    в t._loadModules (vendor.c18e6df....bundle.js: отформатировано: 40474)
    в t._compileModuleAndAllComponents (vendor.c18e6df....bundle.js: отформатировано: 40462)
    в t.compileModuleAndAllComponentsSync(vendor.c18e6df....bundle.js: отформатировано: 40436)
    на e.createComponentFactory(main.dc05ae9....bundle.js: отформатировано: 4789)

Вот мой компонент factory класс:

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

Ответ 1

У меня такое же сообщение об ошибке. Обходной путь, который я нашел, заключается не в использовании декоратора для модуля времени выполнения.

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 В моем случае все решено.

Ответ 2

К сожалению, на данный момент это кажется невозможным (я постараюсь сохранить ответ в курсе), ни с помощью Angular 2.x, ни с Angular 4 beta.
Проблема состоит в том, что определение динамического компонента содержит ссылки на файлы (шаблон, таблицы стилей), которые больше не могут быть устранены во время выполнения с запущенным ранее компилятором AOT.
Но также, если компонент или модуль не будут содержать ссылки на файлы, текущий Angular код не позволяет действительно динамически создавать компоненты. Он просто не находит метаданные, которые создаются во время выполнения.

Суммируя проблему, существует 3 уровня создания динамических компонентов:

  • Определите компонент статически и включите его в NgModule, который компилятор AOT может найти во время компиляции AOT. Такой компонент можно в любой момент создать без проблем. (См. ComponentFactoryResolver и т.д.)
  • Определите тело компонента статически (код и т.д.), но позвольте иметь динамические шаблоны и/или стили (т.е. шаблон создается в коде только тогда, когда это необходимо). Это также требует, чтобы NgModule был скомпилирован во время выполнения. В настоящее время это возможно только тогда, когда не используется компилятор AOT и представляет проблему, которую я опубликовал здесь.
  • Определите полный компонент динамически, включая код и шаблоны. Это не то, что здесь предназначено и может даже пойти далеко. Но, возможно, у кого-то тоже есть эта проблема.

По моему мнению, проблема номер 2 может быть решена. Команда Angular говорит, что, поскольку она AOT, она может компилировать только те вещи, которые статически известны во время компиляции AOT, но я не согласен.
Я мог подумать о возможности компиляции AOT "заглушки" такого компонента, который затем создается при помощи динамического шаблона или таблицы стилей, когда это необходимо. Возможно, потребуется использовать новое свойство для аннотации @Component или совершенно новой аннотации, такой как @DynamicComponent, но это представляется мне возможным. Я не знаю, нужны ли те же изменения для объявления @NgModule, но я предполагаю, что они будут.