You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的多角色根路由实现

问题分析

你当前的实现存在两个核心问题:

  1. useFactory中同步调用authService.hasRole()可能无法获取实时角色状态(比如角色信息从异步接口加载时)
  2. 动态拼接路由会导致根路径重复定义,路由匹配优先级不可控

正确实现步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 20:07:01