实现动态模块时,@NgModule静态forRoot [英] @NgModule static forRoot when implementing dynamic modules

查看:121
本文介绍了实现动态模块时,@NgModule静态forRoot的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在@NgModule中有以下代码:

@NgModule({
 declarations: [
   AppComponent,
   DashboardComponent
 ],
 imports: [
   BrowserModule,
   BrowserAnimationsModule,
   ClarityModule,
   RouterModule.forRoot([
     {
       path: '', redirectTo: 'dashboard', pathMatch: 'full'
     },
     {
       path: 'dashboard', component: DashboardComponent
     },
   ], {useHash: true}),
   LibraryTestModule.forRoot(ServicetestService),
   HttpModule
 ],

如果可以看到,我正在将ServicetestService注入LibraryTestModule.

但是在我的情况下,我使用以下代码使用system.js动态加载此模块:

// now, import the new module
    return SystemJS.import(`${url}`).then((module) => {
        console.log(module);

        return SystemJS.import(`${url}`).then((module) => {
            console.log(module);
            return this.compiler.compileModuleAndAllComponentsAsync(module[`${moduleInfo.moduleName}`]).then(compiled => {
                console.log(compiled);
                return module;
            });
        });
    });

现在,在动态加载模块时,有什么方法可以将ServiceTestService注入到 compileModuleAndAllComponentsAsync 方法中?

解决方案

探索了一段时间之后,我认为我已经找到了解决方案.

widget.module.ts

 export const DYNAMIC_CONFIG = new InjectionToken('DYNAMIC_CONFIG');

@NgModule({
  imports: [
    CommonModule,
  ],
  declarations: [WidgetComponent],
  entryComponents: [WidgetComponent]

  providers: [
  ]
})
export class WidgetModule { }
 

现在让我们假设您正在将该模块动态加载到Shell组件中:

shell.component.ts

 ngOnInit () {
    const parentInjector = Injector.create({
        providers: [
            { provide: DYNAMIC_CONFIG, useValue: { message: 'CUSTOM VALUE PROVIDED BY THE CONSUME!' } }
        ],
        parent: this.inj
    });

    import('./widget/widget.module.ts')
        .then(m => {
            this.compiler.compileModuleAndAllComponentsAsync(m.WidgetModule)
                .then(moduleWithCompFactories => {
                    const module = moduleWithCompFactories.ngModuleFactory.create(parentInjector);

                    /**
                     * Adding the components from the lazy-loaded module
                     * into the current view
                     */
                    this.vc.createComponent(moduleWithCompFactories.componentFactories[0], 0, parentInjector);
                })
        })

}
 

shell.component.html

 <ng-container #vc></ng-container>
 

如您所见,如果您具有依赖性(由模块消费者提供),将会在该模块的组件中使用,并且您使用compileModuleAndAllComponentsAsync方法,除非其他注入器,否则这些组件将无法访问该依赖项是手动创建的.

这是因为,正如您通过方法名称所知,这些组件已经被编译,因此,除了在显式中定义的那些依赖项之外,您不能动态添加其他依赖项.模块.


如果模块内的组件依赖于所提供的依赖关系,则可以通过仅先编译模块(compileModuleAsync)然后分别编译每个组件来实现此目的(这听起来很繁琐,但是我可以向您保证,会喜欢使用这些东西).这样,他们将能够在运行时注入任何动态提供的依赖项,事件.

widget.module.ts

 @NgModule({
  imports: [
    CommonModule,
    // RouterModule.forChild([{ path: '', component: WidgetComponent }])
  ],
  declarations: [WidgetComponent],

  // No need to use this when using `compileModuleAndAllComponentsAsync`
  entryComponents: [WidgetComponent],
  providers: [
    {
      provide: 'widgets',
      useValue: [
        {
          name: 'widget1',
          component: WidgetComponent,
        },
      ],
    }
  ]
})
export class WidgetModule { }
 

shell.component.ts

 ngOnInit () {
    const parentInjector = Injector.create({
        providers: [
            { provide: DYNAMIC_CONFIG, useValue: { message: 'CUSTOM VALUE PROVIDED BY THE CONSUME!' } }
        ],
        parent: this.inj
    });

    import('./widget/widget.module.ts')
        .then(m => {
            this.compiler.compileModuleAsync(m.WidgetModule)
                .then(factory => {
                    const module = factory.create(parentInjector);

                    const [firstWidget] = module.injector.get('widgets');

                    const componentFactory = module.componentFactoryResolver.resolveComponentFactory(firstWidget.component);

                    this.vc.createComponent(componentFactory);
                });
        })

}
 

现在,每次要将其他组件添加到模块中时,请确保将其添加到entryComponents组件数组中,以便可以使用module.injector.get()检索它们. /p>

这是一个StackBlitz 示例.

I have this code in @NgModule:

@NgModule({
 declarations: [
   AppComponent,
   DashboardComponent
 ],
 imports: [
   BrowserModule,
   BrowserAnimationsModule,
   ClarityModule,
   RouterModule.forRoot([
     {
       path: '', redirectTo: 'dashboard', pathMatch: 'full'
     },
     {
       path: 'dashboard', component: DashboardComponent
     },
   ], {useHash: true}),
   LibraryTestModule.forRoot(ServicetestService),
   HttpModule
 ],

If you can see i am injecting the ServicetestService to the LibraryTestModule.

But in my case i am loading this module dynamically using system.js using the code below:

// now, import the new module
    return SystemJS.import(`${url}`).then((module) => {
        console.log(module);

        return SystemJS.import(`${url}`).then((module) => {
            console.log(module);
            return this.compiler.compileModuleAndAllComponentsAsync(module[`${moduleInfo.moduleName}`]).then(compiled => {
                console.log(compiled);
                return module;
            });
        });
    });

Now is there any way to inject the ServiceTestService inside the compileModuleAndAllComponentsAsync method when loading the modules dynamically ??

解决方案

After exploring a while, I think I've arrived at a solution.

widget.module.ts

export const DYNAMIC_CONFIG = new InjectionToken('DYNAMIC_CONFIG');

@NgModule({
  imports: [
    CommonModule,
  ],
  declarations: [WidgetComponent],
  entryComponents: [WidgetComponent]

  providers: [
  ]
})
export class WidgetModule { }

And now let's presume that you're dynamically loading this module into a shell component:

shell.component.ts

ngOnInit () {
    const parentInjector = Injector.create({
        providers: [
            { provide: DYNAMIC_CONFIG, useValue: { message: 'CUSTOM VALUE PROVIDED BY THE CONSUME!' } }
        ],
        parent: this.inj
    });

    import('./widget/widget.module.ts')
        .then(m => {
            this.compiler.compileModuleAndAllComponentsAsync(m.WidgetModule)
                .then(moduleWithCompFactories => {
                    const module = moduleWithCompFactories.ngModuleFactory.create(parentInjector);

                    /**
                     * Adding the components from the lazy-loaded module
                     * into the current view
                     */
                    this.vc.createComponent(moduleWithCompFactories.componentFactories[0], 0, parentInjector);
                })
        })

}

shell.component.html

<ng-container #vc></ng-container>

As you can see, if your dependency(which is provided by the consumer of the module) will be used across your components that are part of that module and you use the compileModuleAndAllComponentsAsync method, the components won't be able to access that dependency, unless another injector is manually created.

This is because, as you can tell by the name of the method, the components will already be compiled, so you can't add another dependencies on the fly, besides those defined explicitly in the module.


If the components inside the module depend on the provided dependency, you can achieve that by only compiling the module first(compileModuleAsync) and then compile each component, individually(it might sound tedious, but I can assure you that you will enjoy working with this stuff). This way, they will be able to inject any dynamically provided dependency, event at runtime.

widget.module.ts

@NgModule({
  imports: [
    CommonModule,
    // RouterModule.forChild([{ path: '', component: WidgetComponent }])
  ],
  declarations: [WidgetComponent],

  // No need to use this when using `compileModuleAndAllComponentsAsync`
  entryComponents: [WidgetComponent],
  providers: [
    {
      provide: 'widgets',
      useValue: [
        {
          name: 'widget1',
          component: WidgetComponent,
        },
      ],
    }
  ]
})
export class WidgetModule { }

shell.component.ts

ngOnInit () {
    const parentInjector = Injector.create({
        providers: [
            { provide: DYNAMIC_CONFIG, useValue: { message: 'CUSTOM VALUE PROVIDED BY THE CONSUME!' } }
        ],
        parent: this.inj
    });

    import('./widget/widget.module.ts')
        .then(m => {
            this.compiler.compileModuleAsync(m.WidgetModule)
                .then(factory => {
                    const module = factory.create(parentInjector);

                    const [firstWidget] = module.injector.get('widgets');

                    const componentFactory = module.componentFactoryResolver.resolveComponentFactory(firstWidget.component);

                    this.vc.createComponent(componentFactory);
                });
        })

}

Now, every time you want to add another component to your module, make sure to add it to entryComponents and to your component array so that you can retrieve them using module.injector.get().

Here is a StackBlitz example.

这篇关于实现动态模块时,@NgModule静态forRoot的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆