Angular懒加载模块中解析器能否使用模块内同实例服务?
问题:懒加载模块中解析器与组件共享服务实例的实现方式
我有一个懒加载模块,所有组件已声明,路由通过RouterModule.forChild()配置。部分路由的解析器会预取数据并调用服务修改父组件头部,相关代码如下:
解析器代码
//Imports... @Injectable({ providedIn: 'root', }) export class MyVendorHeaderResolver implements Resolve<Header> { constructor(private vendorHeaderService: VendorHeaderService) {} resolve( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<Header> { const title: string = 'MainNav.NavMenu.Vendors.Action.MyVendor'; const breadcrumbs: Breadcrumb[] = [ { displayText: 'MainNav.NavMenu.Vendors.Title', link: '..' }, { displayText: 'MainNav.NavMenu.Vendors.Action.MyVendor', link: route.routeConfig.path, }, ]; // 修改主页面组件的头部 this.vendorHeaderService.changeHeader({ title, breadcrumbs } as Header); // 如果上面的代码能使用模块内的服务实例,就不需要返回数据 return of({ title, breadcrumbs } as Header); } }
服务代码
//imports @Injectable({ providedIn: 'root', }) export class VendorHeaderService { private header$ = new Subject<Header>(); constructor() {} headerChange(): Subject<Header> { return this.header$; } changeHeader(header: Header) { this.header$.next(header); } }
路由模块代码
const routes: Routes = [ { path: '', redirectTo: 'vendors/vendors-categories', pathMatch: 'full', }, { path: 'vendors', component: VendorsComponent, children: [ { path: 'vendors-categories', component: VendorsCategoriesComponent, canActivate: [VendorsCategoriesGuard], resolve: { header: VendorCategoryHeaderResolver, categories: VendorsCategoriesResolver, }, children: [ { path: 'new', component: VendorCategoryDialogEntryComponent, }, { path: ':vendorCategoryId', component: VendorCategoryDialogEntryComponent, canActivate: [VendorCategoryGuard], resolve: { categoryDialogData: VendorCategoryDialogInfoResolver, }, }, ], }, ], }, ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], }) export class VendorsAndCustomersRoutingModule {}
核心问题:我希望该服务仅在懒加载模块中提供,但因解析器需要使用它而无法实现。请问:能否让解析器使用与模块组件相同的服务实例,而无需通过provideIn: 'root'将服务暴露到外部? 目前我将服务配置在根注入器中功能正常,但服务会暴露至应用全局。
解决方案
1. 调整服务的提供方式
移除VendorHeaderService的providedIn: 'root'配置,改为由懒加载模块提供:
//imports @Injectable() // 移除providedIn: 'root' export class VendorHeaderService { private header$ = new Subject<Header>(); constructor() {} // 建议返回Observable,避免外部直接调用next方法 headerChange(): Observable<Header> { return this.header$.asObservable(); } changeHeader(header: Header) { this.header$.next(header); } }
2. 在懒加载模块中注册服务与解析器
找到对应的懒加载模块(如VendorsAndCustomersModule),在@NgModule的providers数组中声明服务和所有相关的解析器、守卫,确保它们归属模块级注入器:
@NgModule({ declarations: [ VendorsComponent, VendorsCategoriesComponent, VendorCategoryDialogEntryComponent // 其他模块内组件 ], imports: [ VendorsAndCustomersRoutingModule, // 其他必要导入 ], providers: [ VendorHeaderService, MyVendorHeaderResolver, VendorCategoryHeaderResolver, VendorsCategoriesResolver, VendorsCategoriesGuard, VendorCategoryGuard, VendorCategoryDialogInfoResolver // 所有相关的解析器、守卫 ] }) export class VendorsAndCustomersModule {}
3. 修改路由配置,指定解析器/守卫的获取方式
路由器默认从根注入器获取解析器和守卫实例,因此需要在路由配置中使用useClass语法,明确告诉路由器从当前模块的注入器获取实例:
const routes: Routes = [ { path: '', redirectTo: 'vendors/vendors-categories', pathMatch: 'full', }, { path: 'vendors', component: VendorsComponent, children: [ { path: 'vendors-categories', component: VendorsCategoriesComponent, canActivate: [{ useClass: VendorsCategoriesGuard }], resolve: { header: { useClass: VendorCategoryHeaderResolver }, categories: { useClass: VendorsCategoriesResolver }, }, children: [ { path: 'new', component: VendorCategoryDialogEntryComponent, }, { path: ':vendorCategoryId', component: VendorCategoryDialogEntryComponent, canActivate: [{ useClass: VendorCategoryGuard }], resolve: { categoryDialogData: { useClass: VendorCategoryDialogInfoResolver }, }, }, ], }, ], }, ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], }) export class VendorsAndCustomersRoutingModule {}
原理说明
- 懒加载模块加载时会创建独立的模块级注入器,与根注入器完全隔离。
- 服务和解析器注册在模块的
providers中,会被实例化在模块注入器内。 - 路由配置中使用
useClass,会让路由器从当前模块的注入器中获取实例,从而保证解析器和模块内组件使用同一个服务实例,同时服务不会暴露到全局。
内容的提问来源于stack exchange,提问作者mohamed hassan
相关产品推荐
相关产品推荐

