Beginners guide to Jquery + Ruby On Rails
First time I wanted to use Jquery with Ruby on Rails I had some difficulties understanding how it can works.
Sure, you can easily use Jrails, that’ll brings you RJS helpers with Jquery, but I quite like the unobstrusive way, so Jrails is not (for me) the best solutions.
Let’s code together
What do we want?
We are going to simulate a blog post comment system. We need to be able to list the comments/create a new comment/rate a comment/delete a comment.
All this need to work with and without JavaScript enabled.
I deliberately didn’t use any user/role filtering, the goal of the code is to show of to work with Rails and Jquery
Step1: Prepare the app
We want to work with Jquery, so we don’t need the Prototype files anymore. Just remove everything from your public/javascript/ appart application.js.
In your master layout add this bunch of lines
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"></script>
<% if protect_against_forgery? %>
<script type='text/javascript'>
//<![CDATA[
window._auth_token_name = "#{request_forgery_protection_token}";
window._auth_token = "#{form_authenticity_token}";
//]]>
</script>
<% end %>
<%= javascript_include_tag 'application' %>
We are linking Jquery to the google hosted version, and calling the application.js and setting the forgery protection.
Then in your application.js copy and past the content of my default application.js (Also available as a gist)
jQuery.ajaxSetup({ 'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")} })
function _ajax_request(url, data, callback, type, method) {
if (jQuery.isFunction(data)) {
callback = data;
data = {};
}
return jQuery.ajax({
type: method,
url: url,
data: data,
success: callback,
dataType: type
});
}
jQuery.extend({
put: function(url, data, callback, type) {
return _ajax_request(url, data, callback, type, 'PUT');
},
delete_: function(url, data, callback, type) {
return _ajax_request(url, data, callback, type, 'DELETE');
}
});
jQuery.fn.submitWithAjax = function() {
this.unbind('submit', false);
this.submit(function() {
$.post(this.action, $(this).serialize(), null, "script");
return false;
})
return this;
};
//Send data via get if JS enabled
jQuery.fn.getWithAjax = function() {
this.unbind('click', false);
this.click(function() {
$.get($(this).attr("href"), $(this).serialize(), null, "script");
return false;
})
return this;
};
//Send data via Post if JS enabled
jQuery.fn.postWithAjax = function() {
this.unbind('click', false);
this.click(function() {
$.post($(this).attr("href"), $(this).serialize(), null, "script");
return false;
})
return this;
};
jQuery.fn.putWithAjax = function() {
this.unbind('click', false);
this.click(function() {
$.put($(this).attr("href"), $(this).serialize(), null, "script");
return false;
})
return this;
};
jQuery.fn.deleteWithAjax = function() {
this.removeAttr('onclick');
this.unbind('click', false);
this.click(function() {
$.delete_($(this).attr("href"), $(this).serialize(), null, "script");
return false;
})
return this;
};
//This will "ajaxify" the links
function ajaxLinks(){
$('.ajaxForm').submitWithAjax();
$('a.get').getWithAjax();
$('a.post').postWithAjax();
$('a.put').putWithAjax();
$('a.delete').deleteWithAjax();
}
$(document).ready(function() {
// All non-GET requests will add the authenticity token
// if not already present in the data packet
$(document).ajaxSend(function(event, request, settings) {
if (typeof(window.AUTH_TOKEN) == "undefined") return;
// IE6 fix for http://dev.jquery.com/ticket/3155
if (settings.type == 'GET' || settings.type == 'get') return;
settings.data = settings.data || "";
settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(window.AUTH_TOKEN);
});
ajaxLinks();
});
This is my default application.js when I want to work with Jquery.
Basically what brings you these functions are :
Any link for a class get/post/put/delete will be called via ajax only if JavaScript enabled. Same for form with ajaxForm class.
The code
You can find the original code on my github at
http://github.com/spyou/railsjquery/tree/master
I’ve created a comment resource. The comments table has a body and a score field.
Here is the comments_controller.rb
class CommentsController < ApplicationController
#This is a virtual comment manager. There is no user detection, no session
before_filter :load_post
#This is our entry point, I'm just going to display a factice post to comment
def index
@comments = Comment.all
respond_to do |format|
format.html #we only respond to the html request
end
end
def new
@comment = Comment.new
if !request.xhr?
@comments = Comment.all
end
respond_to do |format|
format.html
format.js {render :layout => false}
end
end
def create
@comment = Comment.create(params[:comment])
respond_to do |format|
format.html { redirect_to comments_path() }
format.js {render :layout => false}
end
end
#This is not a "normal" update, I'll use this one to add points to the comment
def update
@comment = Comment.find(params[:id])
@comment.score = @comment.score + 1
@comment.save
respond_to do |format|
format.html { redirect_to comments_path }
format.js {render :layout => false}
end
end
def destroy
@comment = Comment.find(params[:id])
@comment.destroy
respond_to do |format|
format.html { redirect_to comments_path }
format.js {render :layout => false}
end
end
protected
def load_post
@post = "This is a factice post, I hope you like it. Just comment on the bottom!"
end
end
As you can see on it, we handle 2 different types of responses
respond_to do |format|
format.html
format.js {render :layout => false}
end
Format.html will load your [actionName].html.erb, but the format.js will load a page called [actionName].js.erb were you can put all the callback javascript you need.
For example here is my index.html.erb
<%=
#Use a helper to display the current comments of the post
comments_display(@comments)
%>
<div class="newComment">
<%= link_to 'Create a new comment', new_comment_path(),:class => "get"%>
</div>
and the _comment.html.erb
<div id="comment<%= comment.id %>">
<%=
content_tag(:p,
comment.body)
%>
<!-- We are accessing the update function so we use put as class -->
<span class="score"><%= comment.score %></span><span> - <%= link_to 'Add one point!',comment_path(comment),:class => "put" %></span>
<p> - <%= link_to '*Delete this comment',comment_path(comment),:method => :delete,:class => "delete" %></p>
<p><%= comment.created_at %></p>
<hr />
</div>
You can see the use of get/put/delete class.
Here is now the new.js.erb (Where I call a _form.html.erb to render the new comment form)
$(".newComment").html("<%= escape_javascript(render :partial => 'form') %>");
ajaxLinks();
And here is the update.js.erb (Where I’m suppose to update the score of the comment without refresh)
//Update the score
$('#comment<%= @comment.id %> span.score').html("<%= @comment.score %>");
The tests
Well, the problem with the test is, you can test the rendering of you page (in plain text) but you can’t be sure of the javascript execution.
I’m made some simple tests (testing the plain text) that you can find in the test/functional folder.
(Example with the new comment method)
#Testing the comment creation
context "handle :create" do
context "by html" do
setup do
post :create, :comment => { :body => "My comment" }
end
should_change "Comment.count", :by => 1
should_redirect_to("the index"){comments_path()}
end
context "by xhr" do
setup do
xhr :post, :create, :comment => { :body => "My xhr comment" }
end
should_change "Comment.count", :by => 1
should "Display the new comment link" do
assert_match(/new comment/,@response.body)
end
should "display the created new created message" do
assert_match(%(My xhr comment),@response.body)
end
end
end
This is just the beginning, I’m now looking to some Javascript testing solution for Rails, will be part of another tutorial.
In a nutshell
So in a nutshell, what’d learn here is :
- Rails + Jquery is easy
- format.js + file.js.erb and you’r done
- A good application.js can save your ass
Just checkout the complete source code
git clone git@github.com:spyou/railsjquery.git rake gems:install rake db:create

I hope it was useful.
Or you can just install the jrails plugin.
To my opinion, Jrails is good if you have legacy code using RJS helpers that you don’t want to change. If you start a project from scratch, Jrails is going to add you some JS inside the HTML and won’t work neither is JS disabled, which is bad.
This was a great post. I’m getting into Rails development, and it’s cool to be able to set up my favorite js framework along with Rails.
Can you elaborate on how the forgery protection works?
Thanks!
Excellent post. Keep them coming.
Bharat
Why are you not using LiveQuery plugin? What happens to the bindings when you add a new comment?
Bharat
I didn’t use the LiveQuery plugin here, because I wanted to show the more generic way of implementing this function into an existing Rails code. But you can modify the function and use it, I don’t think it’s a problem.
The binding is removing action on click because when you’ll finish your action, you need to call the ajaxLinks function again, as the new links or form render are not binded because just rendered without layout.
Hope it helps.
Hello Nick,
Thanks for the explanation. Follow up questions:
In your application.js, you are using $put and $delete_ jQuery functions. My jQuery in Action book does not show these two methods are listed, but they work fine on my Ubuntu Linux/Firefox 3.0x machine. Where can I find more information on them?
Your example code is very useful because I learn by doing. Do you have other samples of jQuery with Rails?
Bharat
Thanks a lot, Nicolas. I have translated this post in Russian! It is awesome!
http://www.aleksandrov.me/?p=133
good stuff guy, its rocks on my app
Thanks for this very useful info. The put and delete are exactly what I was looking for.
This is great stuff, really appreciate you sharing, it’s helped meget converted over to JQuery much quicker than it should have taken!
Cheers bud!
Awesome job! I’m currently using jrails on a site and feel pretty meh about it being there. I’m glad your post clearly explains how to use jQuery without jrails even though I still think it is an excellent plugin. Also that is a great convention attaching ajax to all links with classes of get/put/etc. I think if UJS is going to be accepted by more of the community then all of us proponents need to find good conventions to flatten the learning curve more and more. Keep up the great work.
As a beginner I feel that this is less of a beginner’s guide than this screencast that explains exactly what is done and instead shows only the essential stuff to get you going: http://railscasts.com/episodes/136-jquery
@Simon, you’re right, Ryan screencast are really simple and an amazing source of inspiration. I’m sad that you found this tutorial hard to follow. Please let me know if I can help.
This is great works like a charm except when i use destroy in a list using Ryan Bates will paginate with ajax code. Once i desroy a post all the pagination links have the destroyed post_id attached to the uri and it throws off the pagination because it can no longer find a post with that id. Any ideas of how to reset the uri after a destroy
What you can do is reload your pagination div in the delete.js.erb. Can you share part of your code if I can help? (nicolas.alpi on gmail.com / gtalk)
Your application.js is great, I have implemented and will be cleaning mine out to accommodate yours!
I have a question though, I am getting a 406 Not acceptable error when clicking a link with a put class.. Any idea why this could be?
Fixed my issue, its a bug with firefox 3.5 :/
nice1 Nico
works a charm!
[quote]
@Simon, you’re right, Ryan screencast are really simple and an amazing source of inspiration. I’m sad that you found this tutorial hard to follow. Please let me know if I can help.
[unquote]
hey there’s nothing wrong with ur tutorial! -the railscast /does/ fill in the gaps 4 us newbs who want2 understand it -but thx2simon it’s now referenced in the comments so np =)
Hi Nicolas
really big thanks for posting this. It has been a huge help and has significantly lowered the entry barrier for using jquery effectively with Rails. Very well explained, and easy to understand.
I have one question if you have time. I’m running into problems when making AJAX calls from within divs and partials that are shown by other AJAX calls.
Is there a particular reason for this? I’m struggling to understand why this might happen in this way.
cheers!
Paul
This works great, but I (of course) have some questions I’m hoping you can help with.
How do you handle the error callback? I have some cases where if the action sees something wrong, or the save fails, I’d like to have a callback [action]_error.js.erb or something similar that can report to the user what happened, with the current implementation it always calls the callback, regardless of success/fail (as far as I can see). I’d like to be able to call ‘return false’ in my action, and have the error callback fired, rather than the success.
Hi Nellboy, thanks for the comment.
I think you problem can be corrected by calling ajax_link:
1. On page load (as in tutorial)
2. On your javascript page load (exemple at the end of new.js.erb).
You can do something like
repond_to |format| do
if @myvar.save
format.js ……
else
format.js { render :layout => false, :partial => “error” }
end
end
I think it should work perfectly.
Thank you for this great article! Your code is concise and very useful, and it works great. With your patterns it will be easier to convert to new Rails 3 conventions. Unobtrusive JavaScript FTW!
Again, thanks a lot!
Hi Nicolas,
at first big thanks for your post. I read that with Rails 3 unobtrusive jQuery helpers will be supported and i would like to know if your basic ajax-application can get improved from this new features in Rails 3?
Greets, Gerrit
Thanks very much for taking the time to put this together, Nicholas. I’ve been making the switch from Prototype and your tut helped me understand jQuery Ajax a lot better. Works great with jQuery UI, too!
Nicholas:
I had a follow-up question:
I’m implementing this as a nested resource in a Post model. Post has_many comments.
My old (Prototype) form read:
post_comments_path(@post), :html => { :id => ‘comment_form’} do |f| %>
My comments controller create action:
def create
@comment = current_user.comments.build params[:comment]
@comment.app_id = params[:app_id]
respond_to do |format|
format.html { redirect_to comments_path() }
format.js {render :layout => false}
end
end
I tried the post_comments_path(@post) and it doesn’t work.
Also, the Cancel button in the _form partial takes you back to the comments_path(). In other words, the comments index. How would I modify that so it stays on the same post “show” page? I would have thought the syntax would be post_comments_path(@post). A little help on syntax for nested resources would be great! Thanks again!