角度8中的延迟加载模块

时间:2019-06-20 12:03:59

标签: angular typescript

我有一个仪表板应用程序,在这里我懒惰地加载小部件(与路线无关)。

我通过定义对象{name: string, loadChildren: string}来做到这一点。然后在我的app.module中,我将做provideRoutes(...)

这将导致cli为每个小部件模块创建一个块。

然后在运行时,我将使用SystemJsModuleLoader加载该字符串并获得一个NgModuleRef

使用我可以从模块创建组件,然后在createComponent上调用ViewContainerRef

以下是该功能:

 loadWidget(
    name: string,
    container: ViewContainerRef,
    widget: Widget
  ): Promise<{ instance: WidgetComponent; personlize?: { comp: any; factory: ComponentFactoryResolver } }> {
    if (this.lazyWidgets.hasOwnProperty(name)) {
      return this.loader.load(this.lazyWidgets[name]).then((moduleFactory: NgModuleFactory<any>) => {
        const entryComponent = (<any>moduleFactory.moduleType).entry;
        const moduleRef = moduleFactory.create(this.injector);
        const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent);

        const comp = container.createComponent(compFactory);
        (<WidgetComponent>comp.instance).widget = widget;
        const personalize = (<any>moduleFactory.moduleType).personalize;
        if (personalize) {
          return {
            instance: <WidgetComponent>comp.instance,
            personlize: {
              comp: personalize,
              factory: moduleRef.componentFactoryResolver,
              injector: moduleRef.injector
            }
          };
        } else {
          return {
            instance: <WidgetComponent>comp.instance
          };
        }
      });
    } else {
      return new Promise(resolve => {
        resolve();
      });
    }

在角度8中,loadChildren更改为导入功能。

您将获得实际的模块实例,而不是NgModuleRef

我认为我可以通过采用该模块来对其代码进行修复,对其进行编译以获得NgModuleRef,然后将其余代码保持相同。

尽管编译器未捆绑,但似乎在AOT模式下。

因此,我现在基本上只能使用所需的组件实例,但是无法将其添加到View容器中。

它需要一个我无法获得的组件工厂解析器。

我想我的问题是如何获取一个组件实例并将其添加到角度8的视图容器中。现在我已经恢复为使用字符串版本的loadChildren,但这只能在版本9发布之前起作用。 / p>

这是编译器无法在AOT中使用的版本

 if (this.lazyWidgets.hasOwnProperty(name)) {
      return this.lazyWidgets[name]().then((mod: any) => {
        const moduleFactory = this.compiler.compileModuleSync(mod);
        const entryComponent = (<any>moduleFactory.moduleType).entry;
        const moduleRef = moduleFactory.create(this.injector);
        const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent);

        const comp = container.createComponent(compFactory);
        (<WidgetComponent>comp.instance).widget = widget;
        const personalize = (<any>moduleFactory.moduleType).personalize;
        if (personalize) {
          return {
            instance: <WidgetComponent>comp.instance,
            personlize: {
              comp: personalize,
              factory: moduleRef.componentFactoryResolver,
              injector: moduleRef.injector
            }
          };
        }

这是一个示例,说明了我如何考虑但没有办法将其添加到ViewContainerRef中。

模块实例实现了一个需要“ entry”属性的接口。

这定义了要加载的实际组件:

  if (this.lazyWidgets.hasOwnProperty(name)) {
      return this.lazyWidgets[name]().then((mod: any) => {

        const comp = mod.entry;
        (<WidgetComponent>comp.instance).widget = widget;
        const personalize = mod.personalize;
        if (personalize) {
          return {
            instance: <WidgetComponent>comp.instance,
            personlize: {
              comp: personalize,
               factory: null //this no longer works:  moduleRef.componentFactoryResolver,
              // injector: moduleRef.injector
            }
          };
        } else {
          return {
            instance: <WidgetComponent>comp.instance
          };
        }
      });
    } 

编辑:

我试图在stackblitz中添加一个示例,但是编译器将我的字符串转换为函数。至少代码更具可读性。这是我在angular 8中所做的事情。我基本上需要一种使用import()而不是魔术字符串的方法。

https://stackblitz.com/edit/angular-9yaj4l

2 个答案:

答案 0 :(得分:5)

在Angular 8中,loadChildren函数的结果在JIT模式下为Promise类型的NgModule在AOT模式下为Promise的{​​{1}}。

考虑到这一点,您可以按以下方式重写服务:

NgModuleFactory

Stackblitz Example

提示: 如有疑问,请始终查看源代码

答案 1 :(得分:0)

似乎在使用路由器时,lazyWidgets const应该不具有name而是具有path属性:

export const lazyWidgets: { path: string, loadChildren: () => .....

否则,您将得到错误:

  

路由“”的配置无效:路由必须具有路径或   指定匹配器