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

路由前检测表单变更的通用方案需求:替代多组件canDeactive实现

统一表单路由离开校验的方案

针对你现在10多个表单组件重复实现canDeactivate的问题,推荐以下几种可统一维护的方案(以Angular为例,其他框架可参考思路迁移):

方案1:接口约定 + 全局CanDeactivate守卫

这是最简洁的方案,通过接口统一组件的校验契约,守卫集中处理核心逻辑:

  1. 定义通用接口
// form-dirty.interface.ts
export interface FormDirtyCheckable {
  hasUnsavedChanges(): boolean;
  confirmLeave(): Promise<boolean>;
}
  1. 实现全局守卫
// form-dirty.guard.ts
import { CanDeactivate, Injectable } from '@angular/router';
import { FormDirtyCheckable } from './form-dirty.interface';

@Injectable({ providedIn: 'root' })
export class FormDirtyGuard implements CanDeactivate<FormDirtyCheckable> {
  canDeactivate(component: FormDirtyCheckable): boolean | Promise<boolean> {
    // 无变更直接放行
    if (!component.hasUnsavedChanges()) return true;
    // 调用组件的确认逻辑
    return component.confirmLeave();
  }
}
  1. 组件仅需实现接口
    每个表单组件不用重复写完整的守卫逻辑,只需要实现两个简单方法:
// user-form.component.ts
import { FormDirtyCheckable } from './form-dirty.interface';
import { NgForm } from '@angular/forms';

export class UserFormComponent implements FormDirtyCheckable {
  @ViewChild('form') private form: NgForm;

  hasUnsavedChanges(): boolean {
    return this.form.dirty;
  }

  confirmLeave(): Promise<boolean> {
    return new Promise(resolve => {
      // 可替换为项目统一的模态框组件
      if (confirm('表单已修改,确定离开吗?')) {
        resolve(true);
      } else {
        resolve(false);
      }
    });
  }
}
  1. 路由配置添加守卫
    给需要校验的路由绑定这个全局守卫即可:
const routes: Routes = [
  {
    path: 'user-form',
    component: UserFormComponent,
    canDeactivate: [FormDirtyGuard]
  }
];

方案2:通用指令 + 全局守卫(解耦组件逻辑)

如果不想让组件实现接口,可以用指令封装表单状态和确认逻辑,组件只需在模板中添加指令即可:

  1. 创建表单校验指令
// form-dirty-check.directive.ts
import { Directive, Input, Host, Self } from '@angular/core';
import { NgForm } from '@angular/forms';

@Directive({ selector: '[appFormDirtyCheck]' })
export class FormDirtyCheckDirective {
  @Input('appFormDirtyCheck') confirmText = '表单已修改,确定离开吗?';

  constructor(@Host() @Self() private form: NgForm) {}

  isDirty(): boolean {
    return this.form.dirty;
  }

  confirmLeave(): Promise<boolean> {
    return new Promise(resolve => {
      if (confirm(this.confirmText)) resolve(true);
      else resolve(false);
    });
  }
}
  1. 修改守卫适配指令
// form-dirty.guard.ts
import { CanDeactivate, Injectable } from '@angular/router';
import { FormDirtyCheckDirective } from './form-dirty-check.directive';

@Injectable({ providedIn: 'root' })
export class FormDirtyGuard implements CanDeactivate<any> {
  canDeactivate(component: any): boolean | Promise<boolean> {
    // 从组件中获取指令实例(组件需通过ViewChild暴露)
    const checkDirective = component.formDirtyChecker;
    if (!checkDirective || !checkDirective.isDirty()) return true;
    
    return checkDirective.confirmLeave();
  }
}
  1. 组件模板与代码改造
<!-- user-form.component.html -->
<form #form="ngForm" appFormDirtyCheck="用户表单已修改,确定离开?">
  <!-- 表单控件 -->
</form>
// user-form.component.ts
import { ViewChild } from '@angular/core';
import { FormDirtyCheckDirective } from './form-dirty-check.directive';

export class UserFormComponent {
  @ViewChild(FormDirtyCheckDirective) formDirtyChecker: FormDirtyCheckDirective;
}

方案3:全局状态管理(跨框架通用思路)

如果是Vue/React等其他框架,可以用全局路由钩子+状态注册的方式:

  • 维护一个全局的unsavedForms集合,每个表单组件初始化时注册自己的变更检测函数和确认函数
  • 在路由跳转前的全局钩子中,遍历集合检查是否有未保存的表单,统一触发确认
  • 组件销毁时从集合中移除自己

关键注意点

  • 替换原生confirm为项目统一的模态框,保证交互一致性
  • 处理表单重置场景:重置后要手动把表单dirty状态设为false
  • 路由配置时只给需要校验的路由绑定守卫,避免不必要的性能消耗

内容的提问来源于stack exchange,提问作者CMS Develop

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 20:15:18