9

我正在构建 rails 4 jQuery 移动应用程序,但我发现很多次,而不是全部,表单被提交两次,导致记录的双重插入。无论是开发还是生产。

我试过删除 UJS 和 turbolinks 但没有任何区别。如果我禁用 ajax,除非刷新浏览器,否则我的应用程序不会将 jQuery css 应用于返回的视图。

我的 javascript 文件没有被调用两次,也没有在 public/assets 中安装任何资产。我没有预编译任何资产。

我想知道这是否可能是在手机上使用鼠标而不是触摸屏的问题?该应用程序虽然只使用 JQM,但也需要在桌面浏览器中可用。

该问题似乎与重复使用相同的 Web 操作有关,例如,将销售项目添加到账单或在单独的 Web 操作中选择菜单项之后的子菜单项。

应用程序.js

//= require jquery
//= require jquery_ujs
//= require clubapp
//= require jquery.mobile
//= require turbolinks

宝石文件

gem 'rails', '4.0.0.rc2'
gem 'jquery-rails'
gem 'jquery_mobile_rails'
4

3 回答 3

3

可能是您的一些 Javascript 侦听器连接了两次。例如,假设您有以下销售项目元素:

<div class="sale-item">
  <!-- inputs -->
  <button class="add">Add Sale Item</button>
</div>

你有一个动态的销售项目列表,你可以通过 javascript 动态地附加这些列表。但是在每次添加时,您都执行以下代码:

// When adding a new sale item:
    $(".sale-item .add").click(function() {
      // ajax request
    });

因此,每当您添加新的销售项目时,都会为 创建一个侦听器,该侦听器$(".sale-item .add")实际上包括页面中的所有提交项目。然后发生的事情是:

  • 第一次: Sale Item 1添加,监听器集
  • 第二次: Sale Item 2添加,为Sale Item 1 Sale Item 2都添加了监听器。所以现在Sale Item 1有两个监听器,会导致重复提交

这是一个演示

确认是否有重复的侦听器:

一旦您能够重现错误,请记下触发双重提交的按钮的 ID 或类别。然后在 Chrome/Firefox 控制台/firebug 中,运行:

$("<element that's triggering double-submits>").data("events")
> Object {click: Array[1]}

如果它显示click: Array[1],那么您只有一个单击侦听器,并且您的代码没有任何问题。如果它显示click: Array[2]或更多,那么它是一个代码问题,你不知何故有双重听众。

双听的原因

  • 您有多个事件正在添加相同的侦听器(即上述示例)
  • 您有两个使用相同/相似类的不同按钮(例如,一个按钮具有类"btn submit",另一个按钮具有类"submit")。因此,您现在通过说添加的侦听$(".submit")器将同时添加到"btn submit""submit"按钮中!

找到存在重复项的位置非常困难,您必须通过仔细检查代码来手动完成。

于 2013-06-25T16:49:37.110 回答
2

我在提交 JQM 表单时也遇到了问题(主要是试图阻止它)。最终我用普通的替换了我的表单提交按钮<input>

一个按钮将调用一个处理程序:

<input type="button" class="fake_submit" onclick="my_handler()" data-role="button" data-theme="e" data-icon="arrow-r" />

handler获取表单内容,修改所需内容并调用我的全局submitter

我的处理程序将是这样的(你也可以on('click', '.fake_submit', function...

myHandler = function () {
    // get the form
    var form = $(this).closest('form'),
        // custom parameter, I'm creating to route on server side
        switcher = form.find('input[name="form_submitted"]').val(),
        // which url to call
        urlToCall= "../services/form_xyz.cfc",
        // custom parameter, which method to call server-side
        method = "process",
        // override default GET
        type = "post",
        // we expect JSON
        returnformat = "JSON",
        // in case we want to changePage when we have the result
        targetUrl = "",
        // serialize the form and attach custom parameters
        formdata = form.serialize()+"&method="+method+"&returnformat="+returnformat,
        // run this function on success
        successHandler = function() {
            // for example
            switch (switcher) {
                case "login":
                    // refersh application to prevent backbutton
                    window.location = 'index.html';
                    break;
                case "logout":
                    window.location = 'login.html';
                    break;
                case "forget":
                    $.mobile.changePage('lost.html', {transition: 'fade' });
                    break;
                case "message_sent":
                    $.mobile.changePage( 'thanks.html', {transition: 'pop', role: "dialog" });
                    break;
                }
            };
    // call the global ajaxhandler  
    ajaxFormSubmit( form, urlToCall, formdata, targetUrl, successHandler, null, "", returnformat, type );
    // for good measure (and because I also started to be paranoid about awol-submits...
    return false;
    });
};

和提交者:

var ajaxFormSubmit = 
    function ( 
        form,
        urlToCall,
        formdata,       // serialized form data
        targetUrl,      // I think I'm not using this at all... 
        successHandler, //
        dataHandler,    // true/false > does the success function require the response sent back
        errorHandler,   // function to be run on server-side errors
        returnformat,
        type            // override default GET if needed
        ){

            $.ajax({
              async: false,
              type: type == "" ? "get" : type,
              url: urlToCall,
              data: formdata,
              dataType: returnformat,
              success: function( objResponse ){
                 // I'm passing back string or object from the server
                 if (objResponse.SUCCESS == true || typeof objResponse == "string" ){
                    // we need the data returned to continue?
                    dataHandler ? successHandler( objResponse ) : successHandler();
             } else {
                    // server side error
                    if ( errorHandler !== "" ){
                        errorHandler();
                    }
                }
            },
            // ajax error  
            error: function (jqXHR, XMLHttpRequest, textStatus, errorThrown) { 
                console.log(error); 
            }
        });
    }

我做了一个相当复杂的应用程序,我正在以这种方式提交所有表单。工作没有错误。如果您对上述内容有任何疑问,请告诉我。

于 2013-06-29T11:13:30.660 回答
1

最后我选择了 Twitter Bootstrap 而不是 jQuery Mobile,所以我不能说这个答案是确定的,但是值得发表一些评论来帮助其他人。

  1. Turbolinks 和 jQuery Mobile 都会触发 ajax 调用并进行 DOM 插入,因此看起来这些库是不兼容的。无论如何,jQuery mobile 可能包含 Turbolinks。

  2. 在我注意到除非刷新页面,否则我的 $(Document).ready javascripts 将无法工作,我发现这个 gem 解决了这个问题。

    宝石'jquery-turbolinks'

我怀疑上述的组合可能会解决这个问题。一个突出的问题是 JQM 似乎缓存了已更新的页面,尽管默认情况下显然不这样做。明确关闭此行为没有帮助。

于 2013-07-21T16:43:16.217 回答