简体   繁体   English

@NgModule static forRoot 实现动态模块时

[英]@NgModule static forRoot when implementing dynamic modules

I have this code in @NgModule:我在@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.如果您可以看到我正在将 ServicetestService 注入 LibraryTestModule。

But in my case i am loading this module dynamically using system.js using the code below:但在我的情况下,我使用下面的代码使用 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;
            });
        });
    });

Now is there any way to inject the ServiceTestService inside the compileModuleAndAllComponentsAsync method when loading the modules dynamically??现在有什么方法可以在动态加载模块时将 ServiceTestService 注入到compileModuleAndAllComponentsAsync方法中?

After exploring a while, I think I've arrived at a solution.经过一段时间的探索,我想我已经找到了解决方案。

widget.module.ts小部件.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 组件中:

shell.component.ts 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 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.如您所见,如果您的依赖项(模块使用者提供)将用于作为该模块一部分组件并且您使用compileModuleAndAllComponentsAsync方法,则组件将无法访问该依赖项,除非手动创建另一个注入器。

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).如果模块内的组件依赖于提供的依赖项,您可以通过首先编译模块( compileModuleAsync )然后单独编译每个组件来实现(这听起来可能很乏味,但我可以向您保证,您会喜欢使用它东西)。 This way, they will be able to inject any dynamically provided dependency, event at runtime.这样,他们将能够在运行时注入任何动态提供的依赖项和事件。

widget.module.ts小部件.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 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() .现在,每次您想向模块添加另一个组件时,请确保将其添加到entryComponents组件数组,以便您可以使用module.injector.get()检索它们。

Here is a StackBlitz example . 这是一个 StackBlitz示例

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM