Angular懒加载模块注入器无法访问MatDialog的问题求解
项目采用复杂懒加载模块结构,简化后代码如下:
路由懒加载页面模块
declarations: [ MyHeaderComponent ], imports: [ SomeModalModule, ... ]
MyHeaderComponent
constructor( private myService: MyService, ... )
SomeModalModule
NgModule({ imports: [ ModalServiceModule, ... ] })
ModalServiceModule
NgModule({ imports: [ MatDialogModule, ... ] })
MyService
@Injectable({ providedIn: 'root' }) export class MyService { constructor( private modalService: ModalService, ... ) }
ModalService
@Injectable({ providedIn: 'root' }) export class ModalService { constructor( private dialog: MatDialog, ... ) }
触发错误
访问路由时抛出异常:
NullInjectorError: R3InjectorError(FoldersModule)[MyService -> MyService -> MyService -> ModalService -> MatDialog -> MatDialog -> MatDialog]:
NullInjectorError: No provider for MatDialog!
将MyService的@Injectable({ providedIn: 'root' })改为@Injectable({ providedIn: 'any' })后可正常运行,但{ providedIn: 'any' }已被废弃,求合规解决方案。
问题核心是根注入器初始化时,MatDialog的提供者未被加载——MatDialogModule仅在懒加载的ModalServiceModule中导入,而MyService和ModalService是根注入器提供的服务,它们依赖的MatDialog在根注入器中找不到对应提供者。以下是几种合规解决方式:
1. 在根模块(AppModule)中导入MatDialogModule
直接将MatDialogModule添加到根模块的imports数组,确保根注入器能获取到MatDialog的提供者:
// app.module.ts import { MatDialogModule } from '@angular/material/dialog'; @NgModule({ imports: [ BrowserModule, MatDialogModule, // 新增导入 // ...其他根模块依赖 ] }) export class AppModule {}
这是最直接的方案,Material核心模块通常建议在根模块导入,避免懒加载模块重复导入引发的冲突。
2. 调整服务的提供范围,绑定到对应模块
将ModalService的providedIn改为ModalServiceModule,让服务和MatDialogModule处于同一注入器层级:
// modal.service.ts import { Injectable } from '@angular/core'; import { ModalServiceModule } from './modal-service.module'; @Injectable({ providedIn: ModalServiceModule }) // 替换为所属模块 export class ModalService { constructor(private dialog: MatDialog) {} }
同时可以将MyService的providedIn改为对应懒加载模块(如FoldersModule),确保服务生命周期和模块绑定:
// my.service.ts import { Injectable } from '@angular/core'; import { FoldersModule } from './folders.module'; @Injectable({ providedIn: FoldersModule }) export class MyService { constructor(private modalService: ModalService) {} }
3. 用forRoot()模式封装ModalServiceModule
为ModalServiceModule实现forRoot()方法,确保根注入器初始化时加载必要依赖:
// modal-service.module.ts import { NgModule, ModuleWithProviders } from '@angular/core'; import { MatDialogModule } from '@angular/material/dialog'; @NgModule({ imports: [MatDialogModule] }) export class ModalServiceModule { static forRoot(): ModuleWithProviders<ModalServiceModule> { return { ngModule: ModalServiceModule, providers: [] // 若需额外提供者可在此添加 }; } }
然后在根模块中调用ModalServiceModule.forRoot(),懒加载模块中正常导入ModalServiceModule:
// app.module.ts import { ModalServiceModule } from './modal-service.module'; @NgModule({ imports: [ // ...其他根模块依赖 ModalServiceModule.forRoot() ] }) export class AppModule {}
这种模式常用于封装需要在根模块和懒加载模块共享的服务/模块,避免依赖加载时机问题。
内容的提问来源于stack exchange,提问作者patrick

