skip to content

jQuery, Rails, and AJAX

3 min read

On a recent Rails based project, cubeless, I was cleaning up some legacy code with the help of Burin. The project is large and has been through several Rails upgrades but wasn’t fully taking advantage of some of “newer” Rails best practices.

The controllers were heavy with lots of inline RJS and actions that catered only to a particular JavaScript function. We set out to keep the JavaScript isolated to the public/javascripts folder and to use REST in conjunction with the respond_to method. Among the many advantages, this made it easier for us to support those users who do not have JavaScript, for whatever reason.

There are a few tweaks we made to allow jQuery’s AJAX methods to play nice with Rails.

The Authenticity Token

First, we always needed to pass along the authenticity_token for requests using POST. We added a global JavaScript variable named AUTH_TOKEN that stored the authenticity_token.

<%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? -%>

Using the ajaxSend method we can make sure that the authenticity_token is always passed for requests using POST like this.

// Always send the authenticity_token with ajax
$(document).ajaxSend(function(event, request, settings) {
    if ( settings.type == 'post' ) {
        settings.data = (settings.data ? settings.data + "&amp;" : "")
            + "authenticity_token=" + encodeURIComponent( AUTH_TOKEN );
    }
});

HTML, not RJS

Next, we wanted Rails to use the js block of the respond_to but actually wanted back a block of HTML specific to what the JavaScript needed. This allows us to keep our JavaScript in the public/javascripts and allow the html block of the respond_to to properly handle HTML requests when JavaScript is not available, for whatever reason.

In a jQuery AJAX call we’d say the following.

$.ajax({
    url: '/post/1/edit', type: 'get', dataType: 'html'
});

Notice we use a dataType of ‘html’ instead of ‘script’. Normally this would land us in the ‘html’ block of respond_to but with the following JavaScript we will instead land in the ‘js’ block of respond_to.

// When I say html I really mean script for rails
$.ajaxSettings.accepts.html = $.ajaxSettings.accepts.script;

This tells jQuery to set the Accepts header to use “text/javascript, application/javascript” so that Rails will think we are requesting RJS.

In Rails we have to be a little more explicit about what we are rendering back to the client by providing the extension. Otherwise it will be looking for an RJS template.

respond_to do |format|
    format.js { render(:partial => 'some_dialog.rhtml', :layout => 'layouts/_dialog.rhtml') }
end

POSTs with no Data

There were some existing POST requests that didn’t actually send any data in this application. If no data is passed then jQuery does not set the Content-Type to “application/x-www-form-urlencoded” as it normally would. This causes some issues for Rails. We added one line to the ajaxSend method we wrote earlier to always make sure that the Content-Type is “application/x-www-form-urlencoded” on POST requests. The updated ajaxSend looks like this.

// Always send the authenticity_token with ajax
$(document).ajaxSend(function(event, request, settings) {
    if ( settings.type == 'post' ) {
        settings.data = (settings.data ? settings.data + "&amp;" : "")
                + "authenticity_token=" + encodeURIComponent( AUTH_TOKEN );
        request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    }
});

Complete Code Example

Here is the complete code example from this article.

// Always send the authenticity_token with ajax
$(document).ajaxSend(function(event, request, settings) {
    if ( settings.type == 'post' ) {
        settings.data = (settings.data ? settings.data + "&amp;" : "")
                + "authenticity_token=" + encodeURIComponent( AUTH_TOKEN );
        request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    }
});

// When I say html I really mean script for rails
$.ajaxSettings.accepts.html = $.ajaxSettings.accepts.script;

I’d like to know what tips and/or tricks you’ve done to enhance the experience of using jQuery with Ruby on Rails.