嗯,一切都在标题中,但我会解释更多:-)
我的 Rails 应用程序包含许多表单(Ajax 化或非 Ajax 化)。
为了防止用户提交两次或多次某些表单,我使用 Javascript。
这是我的 Ajaxified 表单场景:
现在我想添加服务器端代码,以便在用户绕过 javascript 时保持事物真正干净。
有什么建议吗?
我对 4 个场景使用 4 种方法,请首先选择我的 awnser:防止 Rails AJAX 表单中的双重提交
使用 stopImmediatePropagation 并向目标 dom 添加点击事件。
/**
* 防止按钮重复点击。
* NOTICE: #1 需要在作用点之前调用此方法 #2 stopImmediatePropagation 会阻止后面的所有事件包括事件冒泡
* @delay_duration 两次点击的间隔时间
*/
$.fn.preventMultipleClick = function (delay_duration) {
delay_duration = delay_duration || 3000;
var last_click_time_stamp = 0;
var time_duration = 0;
$(this).bind('click', function (event) {
time_duration = last_click_time_stamp ? event.timeStamp - last_click_time_stamp : 0;
//console.debug("preventMultipleClick", last_click_time_stamp, time_duration);
if (time_duration && time_duration < delay_duration) {
event.stopImmediatePropagation();
} else {
//console.debug("skip preventMultipleClick~");
last_click_time_stamp = event.timeStamp;
}
});
};
使用ajax的beforeSend属性。
/**
* 使用:
* 在jquery的ajax方法中加入参数:beforeSend
* 例如:beforeSend: function(){return $.preventMultipleAjax(event, 5000)}
*
* @param event
* @param delay_duration
* @returns {boolean}
*/
$.preventMultipleAjax = function (event, delay_duration) {
delay_duration = delay_duration || 3000;
var target = $(event.target);
var last_click_time_stamp = target.attr("_ajax_send_time_stamp") || 0;
var time_duration = last_click_time_stamp ? event.timeStamp - last_click_time_stamp : 0;
//console.debug("preventMultipleAjax", last_click_time_stamp, time_duration);
if (time_duration && time_duration < delay_duration) {
return false;
} else {
//console.debug("skip preventMultipleAjax~");
target.attr("_ajax_send_time_stamp", event.timeStamp);
return true;
}
};
<%= f.submit "Save annotation", :disable_with => "Saving...", :class => "btn btn-primary", :id => "annotation-submit-button" %>
或者: 禁用:表单对表单元素,按钮等作业,会阻止其上的事件触发
<input type="submit" value="submit" />
<input type="button" value="button" />
<input type="image" value="image" />
这就是宝石:https://github.com/mlanett/redis-lock
Redis.current.lock("#{current_user.id}.action_name") do
# Some code
end
您可以将选项 :disable_with => "Please Wait..." 添加到提交标签。
尝试使用 Redis 锁定并用类似的东西包围你的块
Redis.current.lock("#{current_user.id}.action_name") do
# Some code
end
这是我正在使用的宝石 https://github.com/mlanett/redis-lock
这就是我要做的:
这不会阻止重复提交,但至少会阻止第二次提交的更改。 IE。当用户第二次提交表单时,代码将根据数据库中的令牌检查提交的令牌,如果它们不匹配,则不更新对象。
这对于新创建的对象也有一个缺点(即,如果用户想要创建评论或类似的东西)。但在这种情况下,您可能想要检查同一用户的创建时间间隔,如果小于 5 秒,您将阻止“创建”对象。
这不是一个真正的建议(我不会对反对票感到惊讶),但是如果没有 JS,你的网站仍然可以使用吗?向他显示适当的消息如何,为了正常操作,他需要启用 JS,否则你不会在页面上向他显示大量的表单。
1)最好还显示一些移动指示器,让用户知道正在发生某些事情,他们的请求正在处理。它应该消除大量的双重提交。
2)如果用户禁用了 javascript,您将如何提交“ajaxified”表单?如果网站在没有 JavaScript 的情况下无法正常工作,那么最好只是通知用户(就像 Eimantas 建议的那样)。
编辑
此类指标的一个示例,只是为了清楚我在 1 中的意思。
http://www.netzgesta.de/busy/
不确定这是否有帮助:
在服务器端:
在新加载表格时, set a session['variable']=false //表示表单尚未提交。
提交表单时,检查:
if session['variable'] == true
{
do nothing...
}
else
{
set session['variable'] = true;
//do submit logic here
}
我也遇到了同样的问题,我用一个非常简单的方法解决了它(也许它不正确或者存在我没有注意到的bug,如果你发现请告诉我)
这是我的回答:
$submitButton.on('click', function(e) {
setTimeout(() => {
$(this).attr('disabled', '');
}, 0);
});
我面临的主要问题是,如果我双击原来的按钮,它会提交两次请求并导致不可预测的情况,所以我尝试在单击按钮后立即使用“禁用”属性来阻止该按钮。这是我写的。
// !!this is an incorrect example!!
$submitButton.on('click', function(e) {
$(this).attr('disabled', '');
});
// !!don't copy this!!
我面临的最大问题是,如果我在单击提交按钮后立即禁用它,则提交请求将不会提交,并且页面将挂在那里,因为什么也没有发生。
我认为原因是“disabled”属性阻止了提交请求的提交。(虽然我不知道这两者之间是什么关系...)
所以,我认为禁用事件应该在提交事件之后执行。
据我了解,表单提交是一个Javascript事件,而setTimeout是一个异步方法。由于Javascript是基于事件循环执行的,ajax事件将被放在事件队列的末尾,并且只有在所有同步事件完成后才会执行。
在我的代码中,第一次点击后,提交按钮将在0毫秒后被禁用,这对于人类来说是不可能点击第二次的,问题解决了!
我的情况(Rails 7+),我有
= form_with model:, url:, data: { turbo: false } do |form|`
删除
data: { turbo: false }
解决了问题。