As most of Rails developers, recently I’ve been through a process of unlearning all concepts of older versions of Rails and learning again the new ones of 3. But hey! I must admit that so far it’s been more pleasure than pain as things only get simpler and more natural than they used to be!
Here I’d like to talk about how simple it has become to integrate unobtrusive jQuery to a Rails app. Let’s use as an example a system of comments. I’ll create a simple app to create YouTube like comments:
Start typing in your teminal:
|
1 2 3 |
rails myCommentsApp rails g resource Comment name:string body:text rake db:migrate |
Then we have to include the jQuery library and the jQuery driver file and place it inside /public/javascripts/ (or grab the helpers generator of Code Officer and do it automatically if you wish). Now remove the javascript_include_tag :defaults in the layout and add the following includes:
/app/views/layouts/application.html.erb
|
1 2 3 |
javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js" javascript_include_tag "jquery-rails.js" |
Now let’s add some logic to our program. We’ll start out with an index action that will get all the comments:
/app/controllers/comments_controller.rb
|
1 2 3 4 5 6 7 |
def index @comments = Comment.all respond_to do |format| format.html # index.html.erb format.rss end end |
And the views for the index will look like this:
/app/views/comments/index.html.erb
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<span id="comments_count"><%= pluralize(@comments.count, "Comment") %></span> <div id="comments"> <%= render :partial => @comments, :locals => { :list => true } %> </div> <hr /> <div id="comment-notice"></div> <h2>Say something!</h2> <% form_for Comment.new, :remote => true do |f| %> <%= f.label :name, "Your name" %><br /> <%= f.text_field :name %><br /> <%= f.label :body, "Comment" %><br /> <%= f.text_area :body, :rows => 8 %><br /> <%= f.submit "Add comment" %> <% end %> |
Notice that the new attribute remote is all we need to worry about when creating a form that will submit with Ajax. Rails 3 works with HTML5 attributes, so it only adds the attribute data-remote=”true” to the form and that’s it, the jQuery driver will handle the rest.
We’ll create a partial for the comments:
/app/views/comments/_comment.html.erb
|
1 2 3 4 5 6 7 8 9 |
<%= div_for comment do %> <span class="dateandoptions"> Posted <%=time_ago_in_words(comment.created_at)%> ago<br /> <%= link_to 'Delete', comment_path(comment), :method => :delete, :class => "delete", :remote => true %> </span> <p><b><%= comment.name %></b> wrote:</p> <br /> <%= content_tag(:p, comment.body, :class => "comment-body") %> <% end %> |
So now let’s add the fireworks! That is, the asynchronous creation and deletion of comments along with some trendy effects. We come back to the CommentsController and add the actions:
/app/controllers/comments_controller.rb
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def create @comment = Comment.create!(params[:comment]) flash[:notice] = "Thanks for commenting!" respond_to do |format| format.html { redirect_to comments_path } format.js end end def destroy @comment = Comment.find(params[:id]) @comment.destroy respond_to do |format| format.html { redirect_to comments_path } format.js end end |
And we are done with the logic part! A few javascript lines will end the work:
/app/views/comments/create.js.erb
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* Insert a notice between the last comment and the comment form */ $("#comment-notice").html('<div class="flash notice"><%= escape_javascript(flash.delete(:notice)) %></div>'); /* Replace the count of comments */ $("#comments_count").html("<%= pluralize(Comment.count, 'Comment') %>"); /* Add the new comment to the bottom of the comments list */ $("#comments").append("<%= escape_javascript(render(@comment)) %>"); /* Highlight the new comment */ $("#comment_<%= @comment.id %>").effect("highlight", {}, 3000); /* Reset the comment form */ $("#new_comment")[0].reset(); |
/app/views/comments/destroy.js.erb
|
1 2 3 4 |
/* Eliminate the comment by fading it out */ $('#comment_<%= @comment.id %>').fadeOut(); /* Replace the count of comments */ $("#comments_count").html("<%= pluralize(Comment.count, 'Comentari') %>"); |
Finish! That’s all the effort you need nowadays to a create a Web 2.0 fashionable feature such as this one. Of course, you need some styling with CSS, and you would need tons of more things in the real world (such as antispam, authentication, something to comment about…) but that’s the part I chose to talk about today!
I pushed myCommentsApp to Github in case you want to have a closer look (or download the zip version).
You might be interested in checking out my script for letting user in-place edit your application contents.
Jordi
1106 days ago
Wow that code looks familiar to me!
That’s a very useful code, I think every comment system should be like this by now, people is getting used to it and feels weird the get the old school redirect.
Also, this code is perfectly compatible with Rails 2, except in rails 2 you don’t need the “jquery rails driver” but the jrails plugin, and instead of using form_for with the :remote => true you should use remote_form_for.
PS: The textarea for submitting comments (in your blog, no the example app) has way too big font size! I have like 4 words per line XD
PS2: I like the new imperative to attract people to comment…
Bernat
1106 days ago
Of course it is familiar to you dude, I coded it first for our joint project semantic.cat (http://github.com/bernat/semantic) and copy&pasted after to my blog, so as to be useful to someone else (which may not be the case).
Indeed, the way you code Ajax actions now and before hasn’t changed that much apparently, though it has in the inside. Taking profit of HTML5 attributes now there’s no need to add blocks of javascript code mixed with markup, you redefine everything separately in your “driver”. In fact, you can capture more actions defining new remote handlers almost painlessly, creating as “data-” attributes as you wish.
You’re right about the font-size being too big, I’ll have it change… someday!
Rails 3 upgrade part 4: Prototype helpers and Javascript at MetaGreg
997 days ago
[...] equivalent for Prototype helper plugin yet so that would be an issue like in my case. Based on this jQuery and Rails 3 tutorial, using the jQuery UJS driver looks very [...]
Cristian
982 days ago
Thanks, it has been really useful for me.
Juba
977 days ago
Wow, thanks ! This tutorial was exactly what I was looking for. Now I think I even understand the use of .js.erb files !
v
974 days ago
tes
Cipe
972 days ago
Hi dude! That’s great
Blake
967 days ago
Cheers for the tutorial… helped me get a few cool things working
Flunder
965 days ago
Thanks alot! This was very handy, thanks for putting it up!
chrismealy
964 days ago
Thanks for the tutorial. It was exactly what I needed. Even better, your code on github worked with zero trouble.
Rails 3 upgrade part 4: Prototype helpers and Javascript « Philippine Ruby Users Group
964 days ago
[...] equivalent for Prototype helper plugin yet so that would be an issue like in my case. Based on this jQuery and Rails 3 tutorial, using the jQuery UJS driver looks very [...]
Fabio
960 days ago
Thanks for the article, very useful…
Moshe
959 days ago
I found an issue with this method of using jquery – escape_javascript removes javascript from the partial. What happens if you need javascript in the partial? for example for autocomplete?
Puneet Pandey
946 days ago
Thanks for sharing this quick tutorial… its very helpful.
Regards
Puneet
bernat
946 days ago
Hey Moshe, excuse me I did not see your reply. This was just the simplest of all possible ways to do it but of course you don’t have to escape javascript, once you’ve got the @comment you can display it the way you like and use all the power of jQuery. If you give me a specific example I can try to help.
Sunny
945 days ago
Hi thanks for the guide, helped me a lot. One thing I don’t understand is how the flash message is shown
“flash.delete(:notice)” this part especially seems strange because of the delete word when we’re trying to show it.
bernat
945 days ago
Hello Sunny,
when you display a flash notice you want to do both things at a time: One is to actually display it and the other is to clear it out so it won’t show up at the next request. With flash.delete[:notice] we do both.
zegal
943 days ago
nice, thanks.
Thimo
936 days ago
Hi, thanks for that very nice tutorial
But I have a
I have exectly the same code and it works all fine
problem
expect of the all the things in the create.js.erb :-/ There is no
highlighting or notice… I use Mozilla Firefox 3.6.11 I got the
following failure in the failure console of firefox: Fehler:
uncaught exception: [Exception... "Cannot modify properties of a
WrappedNative" nsresult: "0x80570034
(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN)" location: "JS frame ::
chrome://global/content/bindings/autocomplete.xml ::
onxblpopuphiding :: line 825" data: no] Is there any other way to
get your nice result without that exception?
coda
933 days ago
yaaaayyy! awesome
Doug
927 days ago
This is great, awesome, woo yeah!
Lyadon
922 days ago
something!!!
fernando
921 days ago
uahuuuuu !!!
jordan
910 days ago
awesome
Javix
892 days ago
Nice tutorial. Some improvements are needed nevertheless:
- add validations for a comment(no empty comment body or comment name)
- flash notice need to be fade out after deleting a comment, for example like that:
$(‘.flash.notice’).fadeOut(); #in destroy.js.erb
Jakko
888 days ago
Great stuff!
Pancho
881 days ago
Fantastic tutorial! It’s been really useful
Alejandro
871 days ago
Hi,
i have some prblems. all code works fine, but, if i have a “format.html” on respod_to and in template a button_to with :remote => true, the format.js dont respond, always shows the format.html unless i comment that line.
how can i fix that? maybe i doing something wrong.
thanks.
bernat
871 days ago
I’m not sure I understand your problem. If you add a :remote => true in your template AND you have the jQuery driver for Rails Helpers added in your layout, you should be able to catch a JS request by adding the format.js line in the respond_to block. If it goes to format.html, as you say, it’s because you are doing a regular synchronous request. Probably not have the jquery.rails.js. If you do a JS (AJAX) request you will only notice what happens by looking at your server console.
Pizzicatto
856 days ago
THanx, man.. good mini-tutorial!
josh
851 days ago
good one
Ryan
842 days ago
Thank you. Very informative.
Markus
830 days ago
> As most of Rails developers, recently I’ve been through a
> process of unlearning all concepts of older versions of Rails
> and learning again the new ones of 3.
Is there a good starting point to learn the concepts? I tried to find something, but not sure yet.
I have also tried to run your application. But it doesn’t work. Once I have submitted the form it stops with an error message. However, the record is stored in the database. Do I have to install or copy any js libaries before running the application?
Markus
830 days ago
I just tried again with the zip-file. After extracting, I did:
bundle install
rake db:migrate
rails s
In the browser I add a comment and pressed the submit button. Nothing happened in the browser and in the console I got the following error message.
http://bit.ly/dYHJg7
Any ideas?
Josh
824 days ago
Wow this is very nice!
Bijesh
817 days ago
This is nice tutorial. Facing problem in one case. After AJAX request, I want to redirect_to different page instead of updating RESPONSE in any div, how can we do this ?
zlorfi
814 days ago
Hi,
it seems your
$(“#new_comment”)[0].reset();
doesn’t reset the comment form, nor does
$(“#new_comment”).get(0).reset();
seem to help :/
The rest seems to work.
paul
811 days ago
Thanks for writing this blog entry, everything worked great. I’m unclear about the following statement:
render :partial => @comments, :locals => { :list => true }
I cannot seem to find out where the locals :list is being referenced. Where should I be looking? I poked around the ‘render’ documentation and also div_for and contact_tag_for and was unable to understand how that symbol is being used.
Thanks again!
Tenshi
810 days ago
Nice!
Kleber
810 days ago
Hi paul,
As I understand, rails will look for a file called _comments under your comments folder in your views, and in this file (_comments.html.erb) you should be able to access the comment variable as a list.
So, this way, you don’t have to do an “for” to generate it.
Hope it helps anyway.
Luiz
797 days ago
Thanks for the nice tutorial
vignesh
796 days ago
Thanks for this tutorial….It helped me a lot in developing my own rails app
Mack
787 days ago
This is awesome tutorial!
Chip
775 days ago
Fantastic. Very well done — I searched high and low for something as clear and straightforward as this. I was able to quickly move forward on a project I’ve been working on, and had hit a roadblock. Thank you!
Sathish
772 days ago
I like it your post because i implemented that on my project but i try to add email for gravatar photo will appear in the comment .. it says error how to do it..
Thanks in advances
Rachid
771 days ago
Well done! but why did you not put -J in the first rails calls, that would spare you deleting files
najob
767 days ago
Hi, love the tutorial and the commenting system is very nice.
Mamoun
752 days ago
I love you man thanks a bunch from a junior rails developer
Carlo
751 days ago
Man there is an error:
Missing template comments/create with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:html], :locale=>[:en, :en]} in view paths “/home/carlo/Desktop/myCommentApp/app/views”
Dan
723 days ago
Hi, I like your tutorial. Short and sweet. I have a question. I am trying to create a http link that will invoke a form submit via ajax. How can I do it?
rubylicious
689 days ago
Nicely done sir!
fearless fool
675 days ago
This was a great help — thanks. BTW, a slightly cleaner way to include jQuery in your app:
[1] add the line
gem 'jquery-rails'to your Gemfile[2] execute
bundle installon the command line[3] execute
rails generate jquery:installon the command lineThat will remove the unused prototype code, install jQuery and jquery_ujs. Now you can leave the original
javascript_include_tag :defaultsin views/layouts/application.html.erb.fearless fool
675 days ago
@bernat: I liked your example so much that I made a fork of myCommentsApp and tweaked it to use the latest Rails with postgresql with README instructions on how to deploy it on Heroku. See github link above.
edmond
656 days ago
thanks a lot.
Cory Logan
643 days ago
I really appreciate the simplicity of the tutorial. I still have not yet digested all of it, but it’s in a wonderful bite sized chunk. Thanks!
Guru Prasad
634 days ago
Nice tutorial.
Thanks a lot
~gurufrequent
Pat Shaughnesy
631 days ago
Thanks a lot, Bernat – this was very helpful for me too… nice job writing: just enough info but not more.
Rick
622 days ago
Thanks for the tutorial.
I’m really new to Ruby on Rails, but I was curious. How could the Web page “invoke” the functionality of a form submission, in order to activate Javascript/JQuery code?
Perhaps this is a fundamental axiom with this framework, but I’m very green to RoR.
I currently have Ruby 1.8.7 and Rails 3.0.9. I’ve also installed the “jquery-rails” gem, whereby my application uses JQuery for its Javascript library, rather than Prototype. How would I update information, dynamically, “after” a page loads?
I’m trying to fetch info from another site for an area of a page, after it’s loaded. I don’t want the user to wait for the entire page to load, because the page is waiting for one DIV’s content to render.
Would anyone have any feedback on that? I’ve thought of doing the work you outline in this tutorial, but I don’t want to have to force a user to click on a form link on my page, in order to do that.
Any feedback would be GREATLY appreciated. Thanks in advance RoR community!!
Mark William
562 days ago
Thank you so much for the tutorial.
i liked it very much.
Thank you for sharing.
saurabh udaniya
533 days ago
this is the best lesson of ajax thanks a lot
Anto
390 days ago
Hey man, pretty cool. One caveat though: on the form_for, you forgot an = sign to display the form
should go
true do |f| %>
great work though! cheers!
Anto
390 days ago
Don’t know what happened. here’s the line!
true do |f| %>
Alexis
360 days ago
Great tutorial, but instead of fadeOut, why not removing the element?
Ruby on Rails 3 – Load HTML through AJAX the RJS way (jQuery + UJS) | Easy jQuery | Free Popular Tips Tricks Plugins API Javascript and Themes
354 days ago
[...] could have a look at this short tutorial: http://blog.bernatfarrero.com/jquery-and-rails-3-mini-tutorial/ Tagged: jquery, questions, ruby-on-rails, ruby-on-rails-3 /* * * CONFIGURATION [...]
harshit
285 days ago
Thanks for tutorial..
Ramprabhu
176 days ago
This is really awesome.
I have one question though. what is your rails version ? Because I am using rails 3.2.8 version.Here deleting user comments functionality is not working. While clicking on delete link, it just redirect to the “show” action in controller instead of going to destroy action. I think there is some problem while firing with ajax. It would be appreciate, please help me out.