ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

低代码表单提交前事件识别按钮:四种实测方案详解

低代码表单提交前事件识别按钮:四种实测方案详解 做表单类项目的实施同学八成遇到过这个需求业务员录入物料档案页面上放两个按钮——保存和保存并继续。点保存就退回列表页点保存并继续要留在当前页面继续录下一条。如果只是单纯的按钮跳转逻辑直接在按钮点击事件里写就行。但问题往往没这么简单很多业务规则必须放在提交前事件里做比如单据编码生成、重名校验、动态必填提示。这时候一个非常现实的技术问题就冒出来了提交前事件执行的时候系统到底能不能告诉我们用户刚才点的是哪个按钮我直接用833版本做了几轮实测把能用的、不能用的、容易踩坑的四种方案都过了一遍。结论先放这儿可以识别但方式取决于你怎么拿、在哪个环节拿、拿的是客户端上下文还是服务端上下文。这篇文章把完整思路和可落地的代码都给你适合正在做表单流程优化的实施工程师、低代码开发者和自己折腾项目的业务同学参考。1. 先搞清楚提交前事件在什么环节触发1.1 为什么业务规则偏偏要写在提交前事件里很多人会问判断点了哪个按钮直接在按钮的点击事件里做不就行了为什么非要在提交前事件里绕一圈实际项目里点击事件往往只负责触发提交动作它拿到的是界面层的信息比如按钮本身、当前页面数据。但真正决定业务能不能走下去的校验比如物料编码是否重复、单号是否符合规则、金额是否超出预算这些逻辑通常集中在提交前事件里统一定义。尤其是一个表单有多个入口工具栏按钮、详情页按钮、外部API调用的时候把校验写在提交前事件里才能保证所有入口都走同一套规则。这时候你需要在统一校验之外再根据用户点的是保存还是保存并继续做差异化处理比如保存并继续需要在提交后跳转保存则直接返回列表。如果拿不到按钮信息你就只能把所有入口当成同一种动作处理需求就实现不了。1.2 833版本的事件链路按钮点击后经历了什么我用833版本实测时最直观的感受是它的提交链路是这样的按钮点击 → 前端基础校验 → 提交前事件客户端第一个执行→ 提交请求 → 服务端校验 → 提交前事件服务端执行→ 数据落库 → 提交后事件 → 页面跳转或刷新。需要注意同一个提交前事件833版本在客户端和服务端两边都有。客户端事件主要处理界面交互、临时变量、跳转提示服务端事件处理权限、数据校验、业务规则。你想要的判断点击按钮在客户端事件里做最容易因为此时按钮对象还在当前页面上下文中到了服务端事件触发源已经变成了一次提交请求如果你不在请求参数里带上标识服务端就是从黑盒里接数据很难知道用户按的是哪个键。这个区别很关键后面所有方案都会围绕它展开。2. 识别按钮前先看看事件上下文里有哪些信息2.1 提交前事件参数里包含的常用对象833版本的提交前事件无论客户端还是服务端都会传入一个上下文对象不同项目里它可能叫 context、args 或 e。我建议你拿到事件后第一件事不是写业务代码而是把上下文里有哪些字段先打印一遍。你可以临时加一条日志输出整个对象或者把你关心的字段输出到消息框里。从我的实测来看客户端提交前事件里至少能拿到这几个信息触发控件对象通常叫 trigger 或 sender也就是用户点下的那个按钮当前表单的数据集合所有字段的键值对事件名称固定是提交前事件部分情况下还带有一个可以写入临时变量的空间这里最值钱的就是触发控件对象。它里面包含了按钮的 id、name、text、tag、commandArgument 等信息。判断用户点了哪个按钮本质就是判断触发控件对象的某个属性值。2.2 按钮也是表单控件学会读按钮属性很多人忽略了一个基础事实在低代码平台里按钮本身就是页面上的一种控件。它同样有 id、name、text、caption、visible、enabled 这些属性部分平台还额外支持自定义属性比如命令参数、扩展字段。所以识别按钮的思路其实就是比较按钮的属性值。最直接的是比较 id因为按钮唯一标识最稳定其次是比较 name 或 commandArgument它们可以承载业务含义最不推荐直接比较 text显示文字因为文字可能被前端改过也可能做了多语言还可能出现两个按钮显示相同文字的情况。我在833版本项目里遇到过不止一次文本框的 label 和按钮的 text 被业务人员顺手改了但 id 没有跟着变。只要判断逻辑写的是 id这些改动都影响不到你如果判断逻辑写的是 text改一次文字就崩一次维护成本非常高。3. 四种实测可用的按钮识别方案3.1 方案一命令参数CommandArgument打标记这是我最推荐的方式也是改造量最小的方式。833版本的按钮控件一般会提供 commandArgument 或类似命名的配置项你在设计表单时给保存按钮配一个值比如 fill 或 saveOnly给保存并继续按钮配另一个值比如 saveAndContinue。然后客户端提交前事件里读这个值function beforeSubmit(context) { var trigger context.trigger; var actionType trigger.commandArgument || ; if (actionType saveAndContinue) { // 用户点的是保存并继续 context.clientData.setItem(actionType, saveAndContinue); } else { // 默认按保存处理 context.clientData.setItem(actionType, save); } }服务端提交前事件里再根据客户端写入的临时变量做进一步业务判断String actionType request.getParameter(actionType); if (saveAndContinue.equals(actionType)) { // 提交后跳转到继续新增页面 } else { // 提交后跳转到列表页 }这个方案的优点是解耦。按钮上的命令参数相当于给动作起了一个业务名字即使两个按钮的样式、位置、显示文字都改了判断逻辑仍然稳定。另外命令参数在页面上是不可见的用户不会误改。3.2 方案二读触发按钮的 id 直接判断如果8005版本纯属笔误这里说的始终是833版本或者你们项目当前使用的版本没有暴露 commandArgument那你直接判断触发控件的 id 就行。按钮在表单设计器里通常都有固定的控件标识比如 btnSave 和 btnSaveContinue。客户端提交前事件里这么写function beforeSubmit(context) { var triggerId context.trigger ? context.trigger.id : ; if (triggerId btnSaveContinue) { context.clientData.setItem(actionType, saveAndContinue); } else { context.clientData.setItem(actionType, save); } }这个方法最简单没有额外配置成本。但有一个前提你的按钮必须是那个唯一触发提交动作的控件。如果页面上还有别的控件也能触发提交比如按回车键触发表单提交那么 context.trigger 可能是null甚至可能是别的输入框这时候就判断不出来了。这种情况需要配合方案三做兜底。3.3 方案三页面隐藏字段暂存按钮标识这个方案解决的是事件上下文里拿不到触发源的极端情况。原理很简单在页面上放一个隐藏字段专门用来记录用户点击了哪个按钮。每个按钮的点击事件里先把隐藏字段赋值再执行提交。比如放一个隐藏字段 hdnActionTypeinput typehidden idhdnActionType valuesave /保存按钮点击事件里document.getElementById(hdnActionType).value save;保存并继续按钮点击事件里document.getElementById(hdnActionType).value saveAndContinue;然后提交前事件里不去读触发控件而是读这个隐藏字段的值function beforeSubmit(context) { var actionType context.form.get(hdnActionType) || save; if (actionType saveAndContinue) { // 处理保存并继续 } }这个方案最稳因为它不依赖提交前事件能拿到什么只依赖按钮点击事件先执行。但要注意两个坑第一隐藏字段的值可能因为页面刷新丢失如果你在按钮点击事件里赋值后没有立刻提交页面刷新了值就复位了第二隐藏字段如果被其他逻辑改过或者用户通过控制台手动改了值服务端拿到的标识就可能不准。所以隐藏字段方案更适合做前端跳转逻辑判断不建议把它当作权限校验的唯一依据。3.4 方案四根据请求参数识别服务端方案如果在833版本的服务端提交前事件里你能看到请求参数列表里面通常会带上本次提交的按钮信息。不同平台命名不一样常见的有 submitSource、clickedButton、actionName 等。你可以先把整个请求参数打出来看一眼确认它叫什么名字。String clickedButton request.getParameter(sourceButtonId); if (btnSaveContinue.equals(clickedButton)) { // 保存并继续 }这个方案的优势是服务端直接判断不依赖客户端变量传递链路更短。但在有些版本里服务端拿到的参数名并不是按钮 id而是一个守卫值或令牌需要你自己在客户端提交时做映射。如果你对请求参数结构不熟建议先用日志把参数逐个打印出来再决定用哪个字段。3.5 四种方案对比速查方案实现难度稳定性依赖条件推荐场景方案一命令参数低高按钮支持配置命令参数业务动作语义明确推荐首选方案二按钮 id最低中事件上下文能拿到触发控件快速实现不介意多花运维成本方案三隐藏字段中中高客户端按钮点击事件可控事件上下文拿不到触发源时兜底方案四请求参数中中高服务端能拿到提交来源参数服务端统一逻辑不依赖客户端传值4. 实操过程把按钮识别落地到新建物料表单4.1 表单配置与按钮设置假设我们要做一个物料档案录入表单字段包括物料编码、物料名称、规格型号、单位、备注。页面上放两个按钮一个叫保存一个叫保存并继续。业务要求点保存录入完退回列表点保存并继续录完接着新增下一条同时要弹出提示第2条录入中以此类推。我在这套表单里实操时采用的组合是方案一 方案三双保险。首先在设计器里选中保存按钮在命令参数里填 saveOnly选中保存并继续按钮在命令参数里填 saveAndContinue。同时我在页面上放了一个隐藏字段 actionType初始值为 saveOnly。隐藏字段的初始值为什么要写成 saveOnly而不是空串这是我在实际项目里踩过坑后总结的经验。如果初始值是空串某些低代码平台在提交时不会把空值字段传过去服务端 request.getParameter 会得到 null还得再写判空逻辑。干脆把默认值设为 saveOnly这样即使按钮点击事件没来得及执行也会按保存处理至少不会出现空指针。4.2 两个按钮的点击事件代码保存按钮的点击事件function btnSave_onClick(context) { context.form.set(actionType, saveOnly); context.submit(); // 触发提交 }保存并继续按钮的点击事件function btnSaveContinue_onClick(context) { context.form.set(actionType, saveAndContinue); context.submit(); }这里有个细节值得展开说。有的同学会在按钮点击事件里直接写一堆校验逻辑再决定是否提交。我的做法是校验尽量留给提交前事件点击事件里只干两件事给标识字段赋值、触发提交。这样无论后面按钮是否被替换只要还是通过这里触发提交标识一定会先写进去。4.3 提交前事件里的核心判断客户端提交前事件也就是表单在提交前执行的最后一段客户端逻辑function beforeSubmit(context) { var actionType context.form.get(actionType); var triggerArg context.trigger ? context.trigger.commandArgument : ; if (triggerArg saveAndContinue || actionType saveAndContinue) { context.clientData.setItem(actionType, saveAndContinue); } else { context.clientData.setItem(actionType, saveOnly); } }这样写的好处是双保险优先看命令参数如果 trigger 拿不到就退回去看隐藏字段。只要两条链路里有一条有值就能判断出来。服务端提交前事件用于最终的落库前校验和跳转参数准备String actionType request.getParameter(actionType); if (actionType null) { actionType saveOnly; } // 公共校验逻辑比如编码不能重复 Result validateResult validateMaterialCode(); if (validateResult.isFail()) { return validateResult; } // 把跳转方式放到上下文中供提交后事件使用 setSuccessRedirect(actionType.equals(saveAndContinue) ? /material/create/new : /material/list);服务端只认客户端传过来的 actionType不做按钮控件层面的判断。这是为了保证服务端逻辑足够干净也方便以后接入 API 批量导入、定时任务时复用同一套校验逻辑。4.4 实际运行效果保存并继续提交后页面没有跳回列表而是停留在表单页表单数据被清空保留部分默认值页面顶部出现已保存物料继续录入第2条的提示。点保存提交后页面跳回物料列表列表里出现刚保存的记录。整个过程走下来提交前事件的角色相当于一个交通指挥中心它在数据发车前完成最后一次清点同时根据乘客的计划行程安排不同的出口。乘客想继续坐下一趟车指挥中心就在系统里做好标记乘客想下车指挥中心就放行到站台。5. 常见问题与排查技巧实录5.1 按钮的 CommandArgument 在提交前事件里读不到这个问题最常出现。我排查过几个项目后总结了三个原因第一你配置命令参数的对象不是实际触发提交的按钮。有的表单有工具栏按钮区和行内按钮区明明配置的是工具栏上的按钮但用户点的是行内图标按钮那 trigger 拿到的当然是行内按钮的属性命令参数自然是空的。第二你配置命令参数之后没有重新部署/发布。低代码平台通常有设计态和运行态的区别设计器里改了参数运行环境可能用的还是旧版本。833版本在有些项目中需要点击发布或重新构建才能让参数生效改完不发布运行态拿不到是正常的。第三事件上下文里的属性名不是 commandArgument。不同版本对字段命名差异很大有叫 commandName 的有叫 options 的有叫 tag 的。我建议你把整个 trigger 对象打印出来字段名以实际输出的 key 为准。5.2 事件上下文里的 trigger 是 null如果你在项目里遇到 trigger 为 null通常不是版本问题而是触发的路径不经过按钮。比如用户在输入框里直接按了回车表单自动提交这时候触发源是输入框的回车事件不是按钮点击。又比如表单是通过外部页面调用的比如清单页面勾选多条记录后批量提交这种请求也没有正常意义上的按钮对象。这种情况下我建议的方案是三用隐藏字段做标识。如果连按钮都没经过那标识就只能用默认值也就是说批量提交默认当成保存处理。这个语义要在设计时就明确不然业务上会出现批量提交之后莫名跳走的困惑。5.3 保存并继续按钮触发了两次提交前事件这个问题出现的场景通常是按钮点击事件里既写了 context.submit()按钮自身的属性里又配置了自动提交。或者提交前事件里写了跳转但提交后事件里又写了一次跳转看起来像事件被触发了两次。遇到这种情况先在日志里打印事件时间戳和入参特征。如果两次时间戳相隔几十毫秒大概率是重复提交如果相隔几百毫秒且一次在客户端一次在服务端那是正常的双端链路不是 bug。如果你不想要双端都执行可以通过上下文里的事件阶段判断只在客户端或只写服务端逻辑避免两边重复执行。5.4 隐藏字段方案的值会丢隐藏字段存值导致判断失效常见原因是页面刷新。部分低代码平台的按钮点击事件如果抛了异常会导致页面重新加载隐藏字段的值被还原成初始值。所以赋值操作一定要放在点击事件最前面并且赋值后立刻提交中间不要夹带容易出错的逻辑。还有一个隐蔽问题是多标签页。如果同一个表单可以打开多个标签页比如同时录入两个物料两个标签页共享同一个隐藏字段那么第二个标签页的按钮点击会把第一个标签页的标识覆盖掉。遇到这种场景不要用隐藏字段直接用方案一或者方案二。5.5 建议封装一个当前动作判断函数无论做多少个表单我都建议把按钮判断逻辑抽成公共函数。在833版本里可以放到公共脚本或者全局代码里function getCurrentActionType(context, fieldName) { if (context.trigger context.trigger.commandArgument) { return context.trigger.commandArgument; } if (context.form context.form.get(fieldName)) { return context.form.get(fieldName); } return saveOnly; }有了这个函数每个表单的提交前事件里只需要一行调用var actionType getCurrentActionType(context, actionType);省得每次重复写 if 分支也降低了不同表单之间因为一人一个写法导致的协作成本。后续如果平台升级、属性名变化只需要改这一处公共函数。6. 几个容易踩的坑与个人心得第一个坑是别用按钮显示文字判断。我见过一个项目判断逻辑写的是 if (buttonText 保存并继续)后来业务要求把按钮改名为保存并新建结果这个判断直接失效。按钮 id 和命令参数是稳定的显示文字是给人看的不能用来做程序判断。第二个坑是别只依赖客户端判断。客户端判断再准也挡不住直接调用数据接口的人。如果保存并继续带来的最大差异是跳转逻辑客户端判断就够了如果差异还涉及业务数据的处理逻辑比如保存并继续时要生成一条临时记录那必须把判断逻辑同步到服务端提交前事件以客户端上传的参数为准。第三个坑是别让保存并继续变成特权动作。有些项目里保存并继续意味着跳过某些校验比如允许编码暂时为空。这种需求要慎重校验规则不统一会带来数据质量风险。真要这么设计建议加权限控制至少限定特定角色才能用这个功能。第四个坑是测试时要覆盖所有提交入口。提交前事件写好后别只测两个按钮。还要测列表页打开表单直接保存、外部链接进入表单保存、API 推送草稿后保存。不同入口的 trigger 都不一样你写的兜底逻辑能不能正确处理全在这些边上。我在实际项目里还有个习惯提交前事件里加一行日志把当前动作类型和关键字段值记录下来console.log([beforeSubmit] actionType actionType , validateCode context.form.get(materialCode));这行日志看着不起眼但出问题的时候能省很多时间。特别是用户反馈我明明点了保存并继续数据没留在页面上日志一查发现 actionType 是 saveOnly就知道他的按钮配置或者调用路径有问题不用瞎猜。如果你已经被提交前事件能不能知道用户点了哪个按钮这个问题卡了小半天希望这篇文章能给你一轮清晰的排查思路。先用日志看上下文再选一个顺手方案落地测试多覆盖几个入口这个需求其实不复杂。后面如果你们平台版本升级属性名变化只需要把公共判断函数里的字段名同步一下就行。
返回列表