路由前检测表单变更的通用方案需求:替代多组件canDeactive实现
统一表单路由离开校验的方案
针对你现在10多个表单组件重复实现canDeactivate的问题,推荐以下几种可统一维护的方案(以Angular为例,其他框架可参考思路迁移):
方案1:接口约定 + 全局CanDeactivate守卫
这是最简洁的方案,通过接口统一组件的校验契约,守卫集中处理核心逻辑:
- 定义通用接口
// form-dirty.interface.ts export interface FormDirtyCheckable { hasUnsavedChanges(): boolean; confirmLeave(): Promise<boolean>; }
- 实现全局守卫
// 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(); } }
- 组件仅需实现接口
每个表单组件不用重复写完整的守卫逻辑,只需要实现两个简单方法:
// 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); } }); } }
- 路由配置添加守卫
给需要校验的路由绑定这个全局守卫即可:
const routes: Routes = [ { path: 'user-form', component: UserFormComponent, canDeactivate: [FormDirtyGuard] } ];
方案2:通用指令 + 全局守卫(解耦组件逻辑)
如果不想让组件实现接口,可以用指令封装表单状态和确认逻辑,组件只需在模板中添加指令即可:
- 创建表单校验指令
// 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); }); } }
- 修改守卫适配指令
// 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(); } }
- 组件模板与代码改造
<!-- 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
相关产品推荐
相关产品推荐

