如何防止带turbo_method: :destroy的link_to被双击重复触发?
防止Turbo删除链接重复触发的几种方案
针对你使用Turbo的删除链接,避免快速双击导致多次调用destroy动作,可以从前端防护和后端兜底两个层面处理:
1. 前端即时禁用(最简单的Turbo原生方案)
利用Turbo内置的data-turbo-disable-with属性,请求发起后自动禁用链接并替换文本,直到请求完成:
<%= link_to "X", items_path(@item), data: { turbo_frame: "frame", turbo_method: :delete, turbo_disable_with: "X" # 点击后保持文本不变,也可换成"删除中..." } %>
这个属性会在Turbo发起请求时,将链接设置为不可点击状态,请求结束(成功或失败)后自动恢复。
2. 自定义JS控制链接状态
如果需要更灵活的样式控制,可通过监听Turbo的事件手动禁用/启用链接:
// 监听请求发起前事件,禁用对应删除链接 document.addEventListener('turbo:before-fetch-request', (event) => { const deleteLink = event.target.closest('a[data-turbo-method="delete"]'); if (deleteLink) { deleteLink.style.pointerEvents = 'none'; deleteLink.classList.add('opacity-50'); // 加个半透明样式提示正在处理 } }); // 监听请求结束事件,恢复链接状态 document.addEventListener('turbo:submit-end', () => { document.querySelectorAll('a[data-turbo-method="delete"].opacity-50').forEach(link => { link.style.pointerEvents = ''; link.classList.remove('opacity-50'); }); });
3. 后端幂等性兜底(最可靠的防护)
前端防护可能存在边界情况,后端需要做兜底校验,确保重复请求不会重复执行删除:
def destroy # 用find_by替代find,避免记录不存在时抛出异常 @item = Item.find_by(id: params[:id]) if @item&.destroy # 正常删除后的逻辑,比如Turbo帧更新 respond_to do |format| format.turbo_stream format.html { redirect_to items_path, notice: '条目已删除' } end else # 记录已不存在,直接返回成功状态,不执行重复操作 head :ok end end
这样即使前端防护失效,重复请求到后端时,因为找不到已删除的记录,不会再次执行destroy逻辑,也不会抛出错误。
内容的提问来源于stack exchange,提问作者Justin Tanner
相关产品推荐
相关产品推荐

