实现动态模块时,@NgModule静态forRoot [英] @NgModule static forRoot when implementing dynamic modules
问题描述
我在@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>
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()
.
这篇关于实现动态模块时,@NgModule静态forRoot的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!