Angular 16中如何使用useFactory实现角色动态路由配置
Angular 16独立路由实现多角色根路径动态导航解决方案
我正在使用Angular 16独立路由,希望实现登录后Doctor和Hospital两类角色均通过根路径/访问对应门户的导航逻辑。现有路由配置中,两类角色的根路径路由分别对应各自模块,但在Angular 16中不知如何通过useFactory方法实现该动态路由逻辑。此前在Angular 14中已找到解决方案,也参考了相关技术资料,但仍未明确Angular 16的具体实现方式。
现有代码片段
app.routes.ts路由配置
{ path: '', loadChildren: () => import('@pages/hospital/hospital.module'), canMatch: [authGuard({ requiresAuthentication: true, role: accountType.hospital })], }, { path: '', loadChildren: () => import('@pages/doctor/doctor.module'), canMatch: [authGuard({ requiresAuthentication: true, role: accountType.doctor })], },
app.config.ts应用配置
export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes, withComponentInputBinding(), withPreloading(SelectivePreloadingStrategyService)), provideHttpClient(withInterceptors([serverErrorInterceptor, jwtInterceptor])), provideAnimations(), provideToastr(), ....., importProvidersFrom( TranslateModule.forRoot({ isolate: true, loader: { provide: TranslateLoader, useFactory: createTranslateLoader, deps: [HttpClient], }, }), ), { provide: TitleStrategy, useClass: TemplatePageTitleStrategy }, { provide: ROUTES, useFactory: () => { let authService = inject(AuthService); let customizedRoutes: Routes = []; customizedRoutes.push({ path: '', canMatch: [authGuard({ requiresAuthentication: true })], loadChildren: () => authService.hasRole() == 3 ? import('@pages/hospital/hospital.module') : import('@pages/doctor/doctor.module'), }); return [...customizedRoutes, ...routes]; }, multi: true, deps: [AuthService] }, ], };
CanMatch守卫代码
export const authGuard = (options: AuthGuardOptions = defaultAuthGuardOptions()): CanMatchFn => { return (_: Route, segments: UrlSegment[]) => { const authService = inject(AuthService); const router = inject(Router); if (options.requiresAuthentication && authService.isAuthenticated) { return true } return options.requiresAuthentication ? router.createUrlTree(['/auth/login'], { queryParams: { returnUrl: segments.map((s) => s.path).join('/'), }, }) : router.createUrlTree(['/']); };
解决方案:适配Angular 16的多角色根路由实现
问题分析
你当前的实现存在两个核心问题:
useFactory中同步调用authService.hasRole()可能无法获取实时角色状态(比如角色信息从异步接口加载时)- 动态拼接路由会导致根路径重复定义,路由匹配优先级不可控
正确实现步骤
1. 完善AuthGuard的角色校验逻辑
给守卫添加角色匹配判断,确保只有对应角色的用户能访问目标路由:
export const authGuard = (options: AuthGuardOptions = defaultAuthGuardOptions()): CanMatchFn => { return (_: Route, segments: UrlSegment[]) => { const authService = inject(AuthService); const router = inject(Router); // 未登录直接跳转登录页 if (options.requiresAuthentication && !authService.isAuthenticated) { return router.createUrlTree(['/auth/login'], { queryParams: { returnUrl: segments.map(s => s.path).join('/') } }); } // 已登录但角色不匹配,跳转无权限页 if (options.role && authService.getRole() !== options.role) { return router.createUrlTree(['/unauthorized']); } return true; }; };
2. 简化路由配置,利用CanMatch自动匹配
不需要在app.config.ts中动态生成路由,直接保留两个根路径路由,Angular会按顺序执行CanMatch守卫,自动匹配符合角色的路由:
// app.routes.ts export const routes: Routes = [ // 登录页 { path: 'auth/login', loadChildren: () => import('@pages/auth/login/login.module') }, // 无权限页面 { path: 'unauthorized', component: UnauthorizedComponent }, // 医院角色根路由 { path: '', loadChildren: () => import('@pages/hospital/hospital.module'), canMatch: [authGuard({ requiresAuthentication: true, role: accountType.hospital })], }, // 医生角色根路由 { path: '', loadChildren: () => import('@pages/doctor/doctor.module'), canMatch: [authGuard({ requiresAuthentication: true, role: accountType.doctor })], }, // 404页面 { path: '**', component: NotFoundComponent } ];
3. 确保AuthService状态响应式(处理异步场景)
如果角色信息从API异步加载,需要把角色状态改成响应式,避免守卫初始化时拿不到数据:
// AuthService示例 @Injectable({ providedIn: 'root' }) export class AuthService { private isAuthenticated$ = new BehaviorSubject<boolean>(false); private userRole$ = new BehaviorSubject<AccountType | null>(null); constructor(private http: HttpClient) { this.initAuthState(); } private async initAuthState() { const token = localStorage.getItem('token'); if (token) { try { const userInfo = await this.http.get<UserInfo>('/api/user/info').toPromise(); this.isAuthenticated$.next(true); this.userRole$.next(userInfo.role); } catch (e) { this.logout(); } } } get isAuthenticated(): boolean { return this.isAuthenticated$.value; } getRole(): AccountType | null { return this.userRole$.value; } logout() { localStorage.removeItem('token'); this.isAuthenticated$.next(false); this.userRole$.next(null); } }
对应的异步守卫逻辑:
export const authGuard = (options: AuthGuardOptions = defaultAuthGuardOptions()): CanMatchFn => { return (_: Route, segments: UrlSegment[]) => { const authService = inject(AuthService); const router = inject(Router); return authService.userRole$.pipe( take(1), map(role => { if (!authService.isAuthenticated) { return router.createUrlTree(['/auth/login'], { queryParams: { returnUrl: segments.map(s => s.path).join('/') } }); } if (options.role && role !== options.role) { return router.createUrlTree(['/unauthorized']); } return true; }) ); }; };
核心原理
Angular 16的独立路由系统中,CanMatch守卫会按路由定义的顺序依次执行,只有当守卫返回true时,该路由才会被匹配加载。因此两个根路径路由会分别校验用户角色,匹配成功的路由自动生效,完美实现多角色共享根路径的需求。
内容的提问来源于stack exchange,提问作者Rajnesh
相关产品推荐
相关产品推荐

