handling ajax callbacks in rails3 using jquery

Previously, in rails 2.3.8 i used the prototype-helpers link_to_remote and form_remote_for (amongst others). These had the option to add callbacks as follows: [ruby] link_to_remote "Add to cart", :url => { :action => "add", :id => product.id }, :update => { :success => "cart", :failure => "error" } [/ruby] (an example from the documentation). This example would, upon success update the html-element with class "cart", and upon failure the class "error". The possible callbacks were:
  • :loading: Called when the remote document is being loaded with data by the browser.
  • :loaded: Called when the browser has finished loading the remote document.
  • :interactive: Called when the user can interact with the remote document, even though it has not finished loading.
  • :success: Called when the XMLHttpRequest is completed, and the HTTP status code is in the 2XX range.
  • :failure: Called when the XMLHttpRequest is completed, and the HTTP status code is not in the 2XX range.
  • :complete : Called when the XMLHttpRequest is complete (fires after success/failure if they are present).
Now the modus operandi has changed, instead we write: [ruby] link_to "Add to cart", :url => {:action => "add", :id => product.id}, :remote => true [/ruby] and it seems there is no option to set the callbacks anymore. Instead of a normal html, we now render javascript, like this (in jquery) : [ruby] $('.cart').replaceWith(<%= escape_javascript(render :partial => 'cart') %>) [/ruby] But how do you handle an error situation? Do i handle it in my controller, and use seperate views? It would seem useful to me to somehow be able to mimic the behaviour we had before. Luckily in this article I was able to find the solution. I had already found that in rails.js the following callbacks were checked:
  • ajax:beforeSend : triggered before executing the AJAX request
  • ajax:success : triggered after a successful AJAX request
  • ajax:complete : triggered after the AJAX request is complete, regardless the status of the response
  • ajax:error : triggered after a failed AJAX request, as opposite to ajax:success
But i had no idea how to provide these callbacks. The javascript should be unobtrusive, so this coupling is not done straight in the HTML anymore. From the same article i found a very clear example how to solve this. Take the following Rails 2.3.8 code : [ruby] <% form_remote_tag :url => { :action => 'run' }, :id => "tool-form", :update => { :success => "response", :failure => "error" }, :loading => "$('#loading').toggle()", :complete => "$('#loading').toggle()" %> [/ruby] That translates to this in Rails3 : [ruby] <% form_tag url_for(:action => "run"), :id => "tool-form", :remote => true do %> [/ruby] and inside some javascript (application.js), you bind the events [javascript] jQuery(function($) { // create a convenient toggleLoading function var toggleLoading = function() { $("#loading").toggle() }; $("#tool-form") .bind("ajax:beforeSend", toggleLoading) .bind("ajax:complete", toggleLoading) .bind("ajax:success", function(data, status, xhr) { $("#response").html(status); }); }); [/javascript] For completeness, here is a list of the events and their expected parameters: [javascript] .bind('ajax:beforeSend', function(xhr, settings) {}) .bind('ajax:success', function(data, status, xhr) {}) .bind('ajax:complete', function(xhr, status) {}) .bind('ajax:error', function(xhr, status, error) {}) [/javascript] [UPDATED 7/2/2012] Updated to reflect the new event-names. :loading was renamed to :beforeSend, and :failure was renamed to :error.
Comments
sampath 2010-09-17 19:10:37 UTC

hi, I have implemented above code week before. but I got following error. Template is missing Missing template hotel/search with {:locale=&gt;[:en, :en], :handlers=&gt;[:builder, :rjs, :erb, :rhtml, :rxml, :haml], :formats=&gt;[:html]} in view paths inside the search action, im using partials. i think that was the issue. please help me to solve the issue. when i call localhost:3000/search, its producing error. only error when use Jquery (AJAX). thanks

Nathan 2010-09-18 21:33:57 UTC

Hi Sampath, with only an error without any context i can't help you. The only thing i can guess, is that when using partials your view name starts with an underscore. So <code>render :partial => 'search'</code> actually needs a view called <code>_search</code>. But I could be way off. If you show me the controller-code and view-code i could shed more light. Kind regards, Nathan

Andrew 2010-12-21 09:09:20 UTC

Nice article! What if you want to include the object id like this : (#mydiv_) I can't seem to get the this to work in the application.js file.

Andrew 2010-12-21 09:11:01 UTC

Oops - your form stripped out my code -

Nathan 2010-12-21 13:00:26 UTC

Hi Andrew, not entirely sure what you mean, but i guess you would just replace the <tt>$('#response')</tt> in the last piece of code, by <tt>$('#my_div')</tt>. Doesn't that work for you?

Eduardo 2011-09-04 18:47:48 UTC

First of all, nice article, I was looking exactly for an example using "toggle" function because Im migrating a 2.3.5 Rails app to 3.0.3. Im having a problem "$("my-form").bind is not a function" pointing to the line in which I have ".bind("ajax:loading", toggleAll)"

Jon 2012-02-07 18:13:28 UTC

FYI, in newer versions of rails 3 (at least 3.2.1, but possibly earlier), ajax:loading has changed to ajax:beforeSend. See http://www.alfajango.com/blog/rails-3-remote-links-and-forms/

nathanvda 2012-02-07 21:31:13 UTC

Hi Jon, indeed I have kept my stackoverflow question and answer (http://stackoverflow.com/questions/3501317/rails3-rails-js-and-jquery-catching-success-and-failure-of-ajax-requests) better up to date than this post. Good reminder to up date this blogpost too.

Add comment

Recent comments

Tags