It has been a while since I have written, but I ran across this age old problem in Rails today and wanted to share my simple solution. Users that double click on submit forms, or multi click when things don’t happen fast enough for them, wreak havoc on web applications. In order to prevent the data from being submitted more than once, there are a variety of approaches, here is mine.

The Ruby (Rails)

In addition to form submission, occasionally we want to throttle down Ajax invoking links to single clicks at a time as well. To accomplish this, we need to look at our use of link_to_remote and form_remote_tag (though this would work for all methods that support link_to_remote options).

  1. # form_remote_tag
  2. <% form_remote_tag (:url => {:action => "save_stuff"},
  3.     :condition => "ajax_in_use == false",
  4.     :before => "ajax_in_use = start_ajax(ajax_in_use)",
  5.     :complete => "ajax_in_use = end_ajax(ajax_in_use)",
  6.     :html => {:id => "html_id_for_form"}) do -%>
  7.  
  8.     # form goes here...
  9. <% end -%>
  10.  
  11.  
  12. #link_to_remote
  13. <%= link_to_remote "click me for ajax",
  14.     :url => {:action => "ajax_method_in_controller",:object_id => this_object.id, :additional_url_parameter => true},
  15.     :condition => "ajax_in_use == false",
  16.     :before => "ajax_in_use = start_ajax(ajax_in_use)",
  17.     :complete => "ajax_in_use = end_ajax(ajax_in_use)"
  18. %>

The things to pay attention to here are :condition, :before and :after. The condition determines whether the call to the back end may be sent, which is controlled through a JavaScript boolean. The :before hook sets the JavaScript variable to show that it is use, and the :after hook releases it. Why not just set the variable to true or false? Using a function allows me to layer in additional behavior to make a more pleasant user experience.

The JavaScript

Now that we have our hooks in place, we need to define the JavaScript functions and variables that are being used. These are all included in an external JavaScript file that is included in the layout.

  1. // initialize ajax state
  2. var ajax_in_use = false;
  3.  
  4. // turn ajax calls off
  5. function start_ajax(ajax_in_use) {
  6.     document.body.style.cursor = 'wait';
  7.     return true;
  8. }
  9.  
  10. // allow ajax calls
  11. function end_ajax(ajax_in_use) {
  12.     document.body.style.cursor = 'default';
  13.     return false;
  14. }

As a small nicety, I change the cursor to ‘wait’ while the Ajax calls execute, helping clue the user in that something is happening. The functions also allow a nice way to do something more complicated in the future. There, thats all there is to it. It isn’t fancy or clever, but it seems to work well so far.