Angular 14.2.7未保存更改守卫问题:取消导航后页面空白
Angular 14: 未保存更改守卫取消导航后页面空白问题解决
问题分析
当通过侧边菜单触发导航,在未保存更改守卫弹出的确认框中点击「取消」后,URL保持正确但页面空白,核心原因是Angular默认的RouteReuseStrategy会在导航启动(NavigationStart)后立即卸载当前组件,即使后续守卫返回false取消导航,已卸载的组件也不会被重新渲染。
解决方案
方案1:自定义路由复用策略(推荐)
通过自定义路由复用策略,在导航取消时保留当前表单组件的实例,避免组件被销毁。
步骤1:创建自定义路由复用策略
import { RouteReuseStrategy, DetachedRouteHandle, ActivatedRouteSnapshot } from '@angular/router'; export class FormRouteReuseStrategy implements RouteReuseStrategy { private storedRoutes = new Map<string, DetachedRouteHandle>(); shouldDetach(route: ActivatedRouteSnapshot): boolean { // 仅对表单路由(add/edit)启用组件复用 const path = route.routeConfig?.path || ''; return path.includes('add') || path.includes('edit'); } store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { const key = this.generateRouteKey(route); this.storedRoutes.set(key, handle); } shouldAttach(route: ActivatedRouteSnapshot): boolean { const key = this.generateRouteKey(route); return this.storedRoutes.has(key); } retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null { const key = this.generateRouteKey(route); return this.storedRoutes.get(key) || null; } shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { return future.routeConfig === curr.routeConfig; } private generateRouteKey(route: ActivatedRouteSnapshot): string { // 生成唯一路由标识,区分新增和编辑路由(如add、edit/123) const basePath = route.routeConfig?.path || ''; const idParam = route.params['id'] ? `:${route.params['id']}` : ''; return `${basePath}${idParam}`; } }
步骤2:在根模块中注册策略
import { NgModule } from '@angular/core'; import { RouteReuseStrategy } from '@angular/router'; import { FormRouteReuseStrategy } from './form-route-reuse-strategy'; @NgModule({ providers: [ { provide: RouteReuseStrategy, useClass: FormRouteReuseStrategy } ] }) export class AppModule { }
方案2:替换同步弹窗为异步模态框
如果不想修改路由复用策略,可将原生window.confirm替换为Angular异步模态框(如Angular Material Dialog),确保导航逻辑完全遵循Angular的变更检测周期:
修改守卫代码
import { Injectable } from '@angular/core'; import { CanDeactivate } from '@angular/router'; import { MatDialog } from '@angular/material/dialog'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; export interface CanComponentDeactivate { unsavedChanges?: boolean; } @Injectable({ providedIn: 'root' }) export class UnsavedChangesGuard implements CanDeactivate<CanComponentDeactivate> { constructor(private dialog: MatDialog) {} canDeactivate(component: CanComponentDeactivate): Observable<boolean> | boolean { if (!component.unsavedChanges) { return true; } // 打开自定义确认弹窗 const dialogRef = this.dialog.open(ConfirmLeaveDialogComponent, { data: { title: '未保存的更改', message: '您有未保存的修改,确定要离开吗?' } }); // 弹窗关闭后返回结果 return dialogRef.afterClosed().pipe( map(confirm => confirm === true) ); } }
创建确认弹窗组件
import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @Component({ template: ` <h2 mat-dialog-title>{{data.title}}</h2> <mat-dialog-content>{{data.message}}</mat-dialog-content> <mat-dialog-actions align="end"> <button mat-button (click)="dialogRef.close(false)">取消</button> <button mat-button color="warn" (click)="dialogRef.close(true)">确定离开</button> </mat-dialog-actions> ` }) export class ConfirmLeaveDialogComponent { constructor( public dialogRef: MatDialogRef<ConfirmLeaveDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any ) {} }
额外检查点
- 验证组件销毁状态:在
TripPageComponent中添加ngOnDestroy钩子,确认取消导航时组件是否被销毁,若被销毁则方案1必选。 - 确认表单
dirty状态:确保TripComponent中tripForm.dirty的取值正确,避免守卫逻辑误触发。
内容的提问来源于stack exchange,提问作者Asmaa Gamal
相关产品推荐
相关产品推荐

