8

What I have:

I have a form that I submit via AJAX.

<%= form_for([@map, @annotation], :remote => true ) do |f| %>
   ...
   <%= f.submit "Save annotation", :class => "btn btn-primary", :id => "annotation-submit-button" %>
<% end %>

What I want:

I would like to prevent double submits. Since the form only disappears when the request has successfully completed, users can click the submit button as long as the database hasn't finished writing the data. I don't want that, obviously.

What I've tried:

I tried adding this to the submit button itself – but it doesn't work. The button gets disabled, but no data is sent.

:onclick => "this.disabled = true"

I've also tried adding a click handler to the submit button. This has the same effect as before. No data is actually sent to the controller, but the button is disabled.

$("#annotation-submit-button").click(function(event) {
  $(this).attr("disabled", "disabled");
  return false;
});

Also tried the same without returning false. Nothing happens after the button is disabled.

$("#annotation-submit-button").click(function(event) {
  $(this).prop("disabled", "disabled");
});

I begin to think that this is something Rails-specific?

abhas
  • 5,193
  • 1
  • 32
  • 56
slhck
  • 36,575
  • 28
  • 148
  • 201

4 Answers4

20

Try disable_with in the view like this for Rails 3 and above:

<%= f.submit "Save annotation", :data => {:disable_with => "Saving..."}, ... %>

For Rails 2:

<%= f.submit "Save annotation", :disable_with => "Saving...", ... %>
Justin Tanner
  • 14,062
  • 17
  • 82
  • 103
abhas
  • 5,193
  • 1
  • 32
  • 56
  • 2
    Not working for me for ajax request, nothing happen when click on submit button but works well with normal request – Sandeep Garg Aug 13 '13 at 08:07
2

Disabling the button should work just fine.

Just disable the button in the same function where you execute your ajax-call.

$("#annotation-submit-button").click(function(event) {
  // do ajax call
  $.ajax(...);
  // disable the button
  $(this).prop("disabled", true);
  // prevent the standard action
  return false;
});

However, as of jQuery 1.6 you should use prop() instead of attr().

Edit:

In reply to your comment try omitting the return false; so you don't interrupt the execution of the formsubmit via Rails.

$("#annotation-submit-button").click(function(event) {
  // disable the button
  $(this).prop("disabled", "disabled");
  // do not return false since this stops the event propagation
});
Christoph
  • 50,121
  • 21
  • 99
  • 128
  • I am using Rails UJS — I don't specify `$.ajax(...)` myself. – slhck Jul 16 '12 at 13:58
  • @slhck take a look at my edit and let me know if it was helpful – Christoph Jul 16 '12 at 14:04
  • `disabled` is boolean, so use `$(this).prop("disabled", true)` or simpler `this.disabled = true`. – VisioN Jul 16 '12 at 14:06
  • Thanks for the response and the update. Interestingly, nothing happens, even when removing `return false`. The button is disabled, but the form is not submitted. – slhck Jul 16 '12 at 14:09
  • Well, I'm sorry i can't help further then. I have not enough experience with Rails for that. Doesn't it provide some sort of eventhandler to be executed on form-submit? – Christoph Jul 16 '12 at 14:22
  • Thanks anyway for your help. Seems abhas figured out the Rails way of doing it. I've never come across that option. – slhck Jul 16 '12 at 14:24
1

By jQuery: you can do the following to handle the action you want

$.ajax({
  .....,
  beforeSend: function (){ $('#your-button').attr('disabled', 'disabled'); },
  success: function (){ 
    // Here you can enable the button
  }
});

Hope this may help you

Alaa Badran
  • 1,848
  • 15
  • 19
  • I have no custom `$.ajax` call as I am using Rails. See the comment on Christoph's answer above. – slhck Jul 16 '12 at 14:02
0

all, After review your ideas and talking in my team, we got an solution:

(function ($) {
  /**
   * 使用:
   *   在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;
    }
  };

  /**
   * 防止按钮重复点击。
   * 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;
      }
    });
  };
})(jQuery);

If you like it I will added it as an little plugin~ Thanks for your suggestion~

fantaxy025025
  • 809
  • 8
  • 7
  • A plugin for where? Is this specific to Rails? After all, a single property does this, no need for a lot of jQuery code. – slhck Nov 23 '13 at 11:53