I am building rails 4 jQuery mobile application but I find that many times, not all, forms are being submitted twice, resulting in double insertion of records. Both in development and production.
I have tried removing UJS and turbolinks but doesn't make any difference. If I disable ajax, my application won't apply the jQuery css to the returned view unless the browser is refreshed.
My javascript files are not being called twice and no assets are installed in public/assets. I have not precompiled any assets.
I wonder if this could be a problem of using a mouse instead of touch screen on a mobile? The application, while only using JQM needs to be usable from a desktop browser as well.
The issue appears to be related to using the same web actions repeatedly, for example, adding sale items to a bill or selecting a submenu item after a menu item in separate web actions.
application.js
//= require jquery
//= require jquery_ujs
//= require clubapp
//= require jquery.mobile
//= require turbolinks
gem file
gem 'rails', '4.0.0.rc2'
gem 'jquery-rails'
gem 'jquery_mobile_rails'
It could be that some of your Javascript listeners are getting attached twice. For example, let's say you have the following sale item element:
<div class="sale-item">
<!-- inputs -->
<button class="add">Add Sale Item</button>
</div>
And you have a dynamic list of sale items that you keep on appending dynamically via javascript. But at each addition, you do the following code:
// When adding a new sale item:
$(".sale-item .add").click(function() {
// ajax request
});
So whenever you add a new sale item, a listener is created for $(".sale-item .add")
, which actually includes all submit items in the page. Then what happens is:
Sale Item 1
added, listener setSale Item 2
added, listener added for both Sale Item 1
and Sale Item 2
. So now Sale Item 1
has two listeners, which will cause duplicate submitsHere is a demonstration.
Once you're able to reproduce the bug, note the ID or class of the button that triggers the double-submit. Then in the Chrome/Firefox console/firebug, run:
$("<element that's triggering double-submits>").data("events")
> Object {click: Array[1]}
If it shows click: Array[1]
, then you have only one click listener, and nothing is wrong with your code. If it shows click: Array[2]
or more, then its a code issue, you're somehow having double listeners.
"btn submit"
and another has class as just "submit"
). So the listener that you add by saying $(".submit")
now gets added to both "btn submit"
and "submit"
buttons!Finding the place where the duplicates are present is quite difficult, you'll have to do it manually by checking your code carefully.
I also had problems with a JQM form submission (mostly trying to prevent it). Eventually I replaced my form submit buttons with plain <input>
A button will call a handler:
<input type="button" class="fake_submit" onclick="my_handler()" data-role="button" data-theme="e" data-icon="arrow-r" />
The handler
will get the form contents, modify what is needed and call my global submitter
:
My handler will be something like this (you could also 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;
});
};
And the submitter:
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);
}
});
}
I did a fairly complex application and I'm doing all form submits this way. Works without errors. Let me know if you have any questions regarding the above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With