Rails 5.2.3中disable_with在render js跳转时失效求通用方案
我之前也遇到过这个坑!Rails 自带的 disable_with 对于返回JS跳转的remote表单确实不太友好——它只要收到控制器的200响应就立刻把按钮恢复成可点击状态,但这时候你的跳转JS可能还没执行完,就容易出现用户重复点击的问题。既然多个控制器都有这情况,给你几个通用的解决方案,不用一个个去改:
方案1:全局覆盖Rails UJS的按钮恢复逻辑
Rails UJS是通过ajax:complete事件来触发按钮恢复的,我们可以全局拦截这个事件,判断响应内容里是否包含跳转代码,再决定要不要恢复按钮。
在你的application.js(或者专门的全局JS文件)里添加这段代码:
// 全局修改Rails UJS的disable_with恢复行为 document.addEventListener('ajax:complete', function(event) { const targetElement = event.target; const response = event.detail[0]; // 检查响应的JS内容是否包含页面跳转指令 if (response.responseText.includes('window.location')) { // 如果是跳转请求,直接跳过按钮恢复(页面马上会刷新,按钮会消失) return; } // 其他正常请求,依然按默认逻辑恢复按钮 Rails.enableElement(targetElement); });
这个方案的优点是零侵入,不用修改任何现有视图或控制器代码,所有remote表单都会自动生效。
方案2:自定义全局按钮禁用逻辑(完全替代disable_with)
如果想更灵活地控制按钮状态,比如自定义加载文字、只在请求失败时恢复按钮,可以自己实现禁用逻辑,抛弃Rails自带的disable_with。
首先,视图里保持原有代码即可(不需要额外配置):
= simple_form_for some_model, remote: true do |f| = f.submit 'Save'
然后在全局JS里添加这段代码:
document.addEventListener('DOMContentLoaded', function() { // 找到所有带remote属性的提交按钮 document.querySelectorAll('input[type="submit"][data-remote="true"]').forEach(btn => { const originalText = btn.value; // 请求发送时禁用按钮,修改文字 btn.addEventListener('ajax:send', function() { this.disabled = true; this.value = 'Saving...'; // 可以改成你想要的加载提示 }); // 只有请求出错时才恢复按钮(成功跳转的话页面会刷新,不需要恢复) btn.addEventListener('ajax:error', function() { this.disabled = false; this.value = originalText; }); }); });
这个方案的好处是完全可控,你可以自定义按钮在加载状态下的样式和文字,而且避免了Rails默认逻辑的干扰。
方案3:应急方案——控制器延迟跳转(不推荐)
如果你暂时不想改JS,也可以在控制器里给跳转加个微小延迟,让按钮恢复的动作在跳转之后执行,但这个方法依赖定时器,稳定性差,不推荐长期使用:
def create # 你的业务逻辑代码... # 加100毫秒延迟,让按钮恢复后再跳转(体验不好,仅应急) render js: "setTimeout(() => window.location='#{path_to_redirect}', 100);" end
推荐方案
优先选方案1,它最省心,全局生效且不需要修改现有代码;如果需要自定义按钮状态,再考虑方案2。
内容的提问来源于stack exchange,提问作者TTD
相关产品推荐
相关产品推荐

