Angular深层嵌套懒加载模块路由中CanDeactivate守卫未触发问题
问题复盘
三层嵌套路由结构包含命名router-outlet,所有模块均为懒加载。目标路由GridManagementWindowComponent的CanDeactivate守卫完全不触发,但canActivate正常工作,无论使用组件方法、inject()注入守卫类还是直接引用守卫类的方式都无效。
可能原因及解决步骤
1. 确认导航动作是否真正触发路由离开
CanDeactivate仅在路由对应组件即将被销毁时触发。如果导航只是在同一命名路由/父路由下的子路由切换,或仅修改了其他router-outlet的内容,目标组件并未被卸载,守卫自然不会触发。
- 测试方法:直接导航到完全脱离当前嵌套结构的路由(比如从
settings/screenConfigTab/manualConfig跳转到首页),观察守卫是否触发。若此时触发,说明之前的导航未触发组件卸载。
2. 检查守卫配置的正确性
组件方法方式
确保GridManagementWindowComponent的hasUnsavedChanges()方法返回值符合Angular要求:
// GridManagementWindowComponent 内的方法 hasUnsavedChanges(): boolean | Observable<boolean> | Promise<boolean> { // 替换为实际的未保存更改判断逻辑 return this.settingsForm.dirty; }
守卫类方式
确保守卫类正确实现CanDeactivate接口,且接收组件实例:
import { Injectable } from '@angular/core'; import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable } from 'rxjs'; import { GridManagementWindowComponent } from './grid-management-window.component'; @Injectable({ providedIn: 'root' }) export class GridUnsavedGuard implements CanDeactivate<GridManagementWindowComponent> { canDeactivate( component: GridManagementWindowComponent, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot ): boolean | Observable<boolean> | Promise<boolean> { return component.hasUnsavedChanges(); } }
若未使用providedIn: 'root',需确保守卫在HeatmapConfigModules的providers数组中声明。
3. 处理命名路由的导航拦截
在包含命名router-outlet的嵌套场景中,Angular路由守卫可能不会自动拦截同一命名outlet内的路由切换。可通过监听路由事件手动处理:
在GridManagementWindowComponent中添加路由监听:
import { Router, NavigationStart } from '@angular/router'; import { filter } from 'rxjs/operators'; constructor(private router: Router) { this.router.events.pipe( filter(event => event instanceof NavigationStart) ).subscribe((event: NavigationStart) => { if (this.hasUnsavedChanges() && !confirm('存在未保存更改,确定离开?')) { this.router.navigate([], { skipLocationChange: true }); } }); }
4. 检查路由配置的层级
确认CanDeactivate守卫配置在最终加载组件的路由上,即HeatmapConfigModules的唯一路由:
{ path: '', component: GridManagementWindowComponent, canDeactivate: [GridUnsavedGuard] // 或使用组件方法 [(component) => component.hasUnsavedChanges()] }
同时确认该路由未被其他路由规则覆盖,是最终激活的路由节点。
总结
大概率是导航动作未触发目标组件卸载,或守卫配置存在细微错误。先通过跨层级导航测试守卫是否正常工作,再逐步排查同一层级内的导航拦截逻辑。
内容的提问来源于stack exchange,提问作者Artyomska

