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

如何利用canDeactivate守卫仅在确认刷新/跳转时调用ngOnDestroy?

问题解决:仅在用户确认离开时执行ngOnDestroy内的保存逻辑

问题根源有两个:

  1. 你在ngOnInit里手动通过window.onbeforeunload调用ngOnDestroy,导致不管用户是否确认离开,都会触发保存逻辑;
  2. ngOnDestroy是Angular组件的生命周期钩子,只要组件销毁就会自动执行,当前逻辑没有区分“用户确认离开”和“其他销毁场景”。

修正方案:


1. 移除手动触发ngOnDestroy的代码

删掉ngOnInit中的这行代码:

window.onbeforeunload = () => this.ngOnDestroy();

这行代码会强制触发ngOnDestroy,完全忽略用户的确认选择。

2. 给组件增加确认离开的状态标记,控制保存逻辑

修改ShowQuizComponent,通过状态位区分是否是用户确认后的合法离开:

// 新增状态位,标记是否为用户确认后的合法离开
private isConfirmedLeave = false;

ngOnDestroy(): void {
  // 仅当未结束考试且用户确认离开时,执行保存
  if (!this.end && this.isConfirmedLeave) {
    this.checkAnswers();
  }
}

// 处理Angular内部路由跳转的守卫逻辑
canDeactivate(): Observable<boolean> | boolean {
  if (this.end) {
    this.isConfirmedLeave = true;
    return true;
  }

  const userConfirmed = confirm('ATENÇÃO: Alterações podem não ter sido guardadas. Tem a certeza que deseja sair?');
  if (userConfirmed) {
    this.isConfirmedLeave = true;
    // 也可以在这里直接调用checkAnswers,无需等ngOnDestroy
    // this.checkAnswers();
  }
  return userConfirmed;
}

// 处理浏览器刷新、关闭标签页的场景
@HostListener('window:beforeunload', ['$event'])
handleBeforeUnload(event: BeforeUnloadEvent): void {
  if (!this.end) {
    // 触发浏览器默认的离开确认弹窗
    event.preventDefault();
    event.returnValue = ''; // 兼容多浏览器

    // 注意:浏览器限制下无法获取用户是否确认离开的结果,这里直接执行保存(建议用同步操作)
    this.checkAnswers();
  }
}

3. 简化守卫逻辑

守卫只需要调用组件的canDeactivate方法即可,把确认逻辑放在组件内部:

export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
  canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
    return component.canDeactivate();
  }
}

核心逻辑说明:

  • Angular内部跳转:用户确认离开后,设置isConfirmedLeave为true,ngOnDestroy执行时才会触发保存;
  • 浏览器刷新/关闭:由于浏览器安全限制,无法捕获用户的确认结果,只能直接执行保存(同步操作更可靠,异步操作可能被浏览器拦截)。

内容的提问来源于stack exchange,提问作者André Clérigo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 19:20:30