Angular使用CanActivate校验路由分类参数,异步取数为undefined如何解决
实现思路合理性说明
你想用CanActivate守卫在路由加载前校验分类是否存在的思路是合理的,这是Angular路由守卫的典型使用场景,符合Angular路由设计的规范,只是具体实现的异步逻辑处理和语法细节存在问题。
现有代码的问题点
- 没有利用CanActivate支持异步返回值的特性:Angular的CanActivate接口支持返回
Observable<boolean>、Promise<boolean>、boolean三种类型,你当前只返回同步的boolean,Angular不会等待异步接口返回就会执行后续路由逻辑,自然拿不到categories的值。 - 额外语法错误:
- 分类过滤时错把比较运算符
===写成了赋值运算符= - 判断数组长度时把
length拼写错为lenght - 手动订阅Observable但没有取消订阅,存在内存泄露风险
- 分类过滤时错把比较运算符
正确实现方案
直接让canActivate返回Observable<boolean>,通过RxJS操作符处理异步校验逻辑即可,不需要把分类数据存到类属性里:
import { Injectable } from "@angular/core"; import { ActivatedRouteSnapshot, CanActivate, Router, UrlTree } from "@angular/router"; import { CategoriesService } from "@app/shared/services/categories.service"; import { Observable, of } from "rxjs"; import { map } from 'rxjs/operators'; @Injectable() export class CategoriesGuard implements CanActivate { constructor( private router: Router, private categoriesService: CategoriesService ) { } canActivate(route: ActivatedRouteSnapshot): Observable<boolean | UrlTree> { const categoryFromParam = route.paramMap.get('mainCategory'); // 直接返回接口请求的Observable,Angular会自动订阅并等待结果 return this.categoriesService.getCategories().pipe( map(categories => { // 用some方法判断是否存在匹配项,比filter性能更高 const isCategoryExists = categories.some(x => x.categoryName === categoryFromParam); if (isCategoryExists) { return true; } // 不存在则直接返回UrlTree,是Angular路由守卫推荐的跳转方式 return this.router.parseUrl('/404'); }) ); } }
可选优化
如果分类数据不会频繁变更,还可以增加本地缓存,避免每次触发守卫都请求后端接口:
import { Injectable } from "@angular/core"; import { ActivatedRouteSnapshot, CanActivate, Router, UrlTree } from "@angular/router"; import { CategoriesService } from "@app/shared/services/categories.service"; import { Observable, of } from "rxjs"; import { map } from 'rxjs/operators'; @Injectable() export class CategoriesGuard implements CanActivate { // 分类数据缓存 private cachedCategories: any[] = null; constructor( private router: Router, private categoriesService: CategoriesService ) { } canActivate(route: ActivatedRouteSnapshot): Observable<boolean | UrlTree> { const categoryFromParam = route.paramMap.get('mainCategory'); // 有缓存直接用缓存校验 if (this.cachedCategories) { const isExists = this.cachedCategories.some(x => x.categoryName === categoryFromParam); return of(isExists ? true : this.router.parseUrl('/404')); } // 无缓存则请求接口并存入缓存 return this.categoriesService.getCategories().pipe( map(categories => { this.cachedCategories = categories; const isCategoryExists = categories.some(x => x.categoryName === categoryFromParam); return isCategoryExists ? true : this.router.parseUrl('/404'); }) ); } }
内容的提问来源于stack exchange,提问作者jrywin
相关产品推荐
相关产品推荐

