Form submitted twice, due to :remote= true
In case people are stumbling on this question like I did:
I had the same problem, and sannankhalid's answer didn't fix it, but deleting a locally precompiled application.js in the public/assets
directory did -- the ujs is included twice, so it fires twice. Via https://stackoverflow.com/a/9627690/604093
Ruby on Rails - Form submitted twice due to Ajax
You are sending two request for submitting the form, one is when you write remote => true in the form it will make ajax request and again you are binding an ajax request for submitting the form, remove the remote => true from the form_for.
form :remote = true, submitted twice in IE
It turns out to be that I had older versions on Prototype and rails.js in my application. Upgrading solved the problem.
form submitting twice with :remote = true Rails 3.2
i had run rake assets:precompile and was running my server in development mode. solution was to rake assets:clean and restart the sever
Event.preventDefault() not working, form submitted twice
In Rails form_with() and form_tag() operate differently.
According to this article:
https://m.patrikonrails.com/rails-5-1s-form-with-vs-old-form-helpers-3a5f72a8c78a
"All forms generated by form_with will be submitted by an XHR (Ajax) request by default. There is no need to specify remote: true as you have to with form_tag and form_for."
Hence, either turning remote to false (local: true) or using form_tag solved the issue, as .preventDefault stops the local submit, not ajax submits.
How to keep submit buttons disabled on remote forms until the next page has loaded
What is happening?
- Form is submitted
- rails-ujs disables the button (the
data-disable-with
behaviour) - The form request succeeds
- rails-ujs re-enables the button
- turbolinks-rails makes a request to the redirect location (which might be a slow one, leaving the button in an enabled state)
Solution
We'll need to re-disable the button after step 4. To do this, we'll listen out for the ajax:success
event, and disable it using setTimeout
. This ensures that it will be disabled after Rails has done its thing. (You could use requestAnimationFrame
instead of setTimeout
, but it is not as widely supported.)
To prevent the button from being cached in a disabled state, we'll re-enable it before it is cached. (Note the use of one
rather than on
to prevent the before-cache handler executing more than once.)
I noticed you were using jQuery and jquery-ujs, so I will use functions from those libraries in the code below. Include this somewhere in your main JavaScript file.
jquery-ujs
;(function () {
var $doc = $(document)
$doc.on('submit', 'form[data-remote=true]', function () {
var $form = $(this)
var $button = $form.find('[data-disable-with]')
if (!$button.length) return
$form.on('ajax:complete', function () {
// Use setTimeout to prevent race-condition when Rails re-enables the button
setTimeout(function () {
$.rails.disableFormElement($button)
}, 0)
})
// Prevent button from being cached in disabled state
$doc.one('turbolinks:before-cache', function () {
$.rails.enableFormElement($button)
})
})
})()
rails-ujs / jQuery
;(function () {
var $doc = $(document)
$doc.on('ajax:send', 'form[data-remote=true]', function () {
var $form = $(this)
var $button = $form.find('[data-disable-with]')
if (!$button.length) return
$form.on('ajax:complete', function () {
// Use setTimeout to prevent race-condition when Rails re-enables the button
setTimeout(function () {
$button.each(function () { Rails.disableElement(this) })
}, 0)
})
// Prevent button from being cached in disabled state
$doc.one('turbolinks:before-cache', function () {
$button.each(function () { Rails.enableElement(this) })
})
})
})()
rails-ujs / vanilla JS
Rails.delegate(document, 'form[data-remote=true]', 'ajax:send', function (event) {
var form = event.target
var buttons = form.querySelectorAll('[data-disable-with]')
if (!buttons.length) return
function disableButtons () {
buttons.forEach(function (button) { Rails.disableElement(button) })
}
function enableButtons () {
buttons.forEach(function (button) { Rails.enableElement(button) })
}
function beforeCache () {
enableButtons()
document.removeEventListener('turbolinks:before-cache', beforeCache)
}
form.addEventListener('ajax:complete', function () {
// Use setTimeout to prevent race-condition when Rails re-enables the button
setTimeout(disableButtons, 0)
})
// Prevent button from being cached in disabled state
document.addEventListener('turbolinks:before-cache', beforeCache)
})
Note that this will disable buttons until the next page load on all data-remote
forms with a data-disable-with
button. You may want to change the jQuery selector to only add this behaviour to selected forms.
Hope that helps!
Related Topics
How to Cache a Calculated Column in Rails
How to Validate Members of an Array Field
Active Admin - Refresh Second Drop Down Based on First Drop Down, Ruby on Rails
How to Resolve Rails Model Namespace Collision
Regex Match Everything Up to First Period
Rails: Dynamic Columns/Attributes on Models
Redis or Mongo for Determining If a Number Falls Within Ranges
Importing CSV Quoting Error Is Driving Me Nuts
Heroku Rails 4 Could Not Connect to Server: Connection Refused
Why Are My Bigdecimal Objects Initialized with Unexpected Rounding Errors
Error Running '_Rvm_Make Install'
Can't Install Thrift Gem on Os X El Capitan
Subtract N Hours from a Datetime in Ruby