Andrew Cox


Rails 3.0 unobtrusive Ajax in Rails 2.3.x

Rails 3 is going to bring some awesome unobtrusive JavaScript that allows you to do really powerful stuff with clean, semantic markup. The best part is that you don’t have to wait until Rails 3 is finalized before using it on your Rails 2.3.x project.

Josh Huckabee gives a great overview of how to get unobtrusive jQuery working with Rails 3, but I’m going to show a complete example of how to integrate unobtrusive jQuery with Ajax in a Rails 2.3.4 project.

This simple example will create a complete Rails “To Do” application with a simple Ajax form that adds a new item to a list on the page.

Create your app

$ rails todo

Set up your scaffolding

$ cd todo
$ script/generate scaffold item description:string
$ rake db:migrate

Add the Rails 3 jQuery script

Just download jQuery and the rails.js script from the src directory at jquery-ujs and drop them both in your public/javascripts directory. Then just include it along with the latest version of jQuery.

app/views/layouts/application.html.erb

<%= javascript_include_tag "jquery-1.4.2.min", "rails" %>

Rails requires an authenticity token to do posts back to the server, so you’ll also have to stick these in the head of your page:

app/views/layouts/application.html.erb

<meta name="csrf-token" content="<%= form_authenticity_token %>" />
<meta name="csrf-param" content="authenticity_token" />

Create a new @item for your index action

Since we’re going to be adding items right on the main index page, we need to create a new, empty item:

def index
  @items = Item.all
  @item = Item.new

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @items }
  end
end

Set up your main view with Ajaxified form

Update the index.html.erb file to have the form as well as all items. We’re jumping straight to a partial for the item because it’ll make it easier to render the newly-created items with JavaScript later.

Notice the ‘data-remote’ attribute on the form. That’s all you need to do for the unobtrusive JavaScript to turn your basic form in to an Ajax form. Pretty great stuff.

app/views/items/index.html.erb

<% form_for @item, :html => {'data-remote' => true} do |f| %>
  <%= f.text_field :description %>
  <%= f.submit "Add Item!" %>
<% end %>

<ul id="items">
  <%= render :partial => "item", :collection => @items %>
</ul>

Create an item partial

Create a simple partial to render an item. This will be responsible for rendering all items on page load as well as the newly-created items with Ajax.

app/views/items/_item.html.erb

<li>
  <%= item.description %>
</li>

Update the create action in your controller

To handle the Ajax submission, add a format.js block to the create action in your controller. It should look like:

app/controllers/items_controller.rb

def create
  @item = Item.new(params[:item])

  respond_to do |format|
    if @item.save
      flash[:notice] = 'Item was successfully created.'
      format.js
      format.html { redirect_to(@item) }
      format.xml  { render :xml => @item, :status => :created, :location => @item }
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @item.errors, :status => :unprocessable_entity }
    end
  end
end

Create the JavaScript partial to add your new item to the list

The Ajax request will go to the format.js block on a successful submit, so you just need to add the corresponding create.js.erb to handle adding the newly-created item to the list:

app/views/items/create.js.erb

$('#items').append('<%= escape_javascript(render :partial => "item", :locals => { :item => @item }) %>');

And that’s all there is to it! You’ll probably want to clear out the text box and set the focus after adding terms so you can add many terms in quick succession. No problem! Just add one more line to create.js.erb:

$('#item_description').attr('value', '').focus();

You can take it from there to add error handling, fancy fade-in effects to the new items or whatever. Have fun!