Rails #3: AJAX

In my previous articles, I showed you how to get started using Rails, and how to create model objects that are associated with each other. In this article, we will clean up the way that information is displayed and add support for dynamic HTML, or as the cool kids are calling it these days, AJAX.

A view to die for

First, clean up app/views/articles/show.hml.erb. I am assuming you know enough HTML to be able to make it look reasonably blog like. Following is a suggested starting point. The only thing that is critical to follow allow the text of this tutorial, is the id of the “new_comment” div. This will be used as a target for our AJAX code.

Partially better

The easiest way to use Rails AJAX support is to redraw parts of the view from the server. You can remove many sources of error by making sure that you’re using exactly the same HTML + ERb code to render the new views from the AJAX requests. This is a good place to introduce another Rails technique: Partials.

  1. Replace the code from <% for comment %> to the corresponding <% end %> section with a call to render method like so: <%= render :partial => 'comments/comment', :collection => @article.comments %>
  2. Move the <div> used for the comment to a new file, app/views/comments/_comment.html.erb
  3. The call to render :partial means to call the file app/views/comments/_comment.html.erb for each comment in @article.comment. Each comment will be assigned to the variable comment in turn.
  4. That’s it – your comment code is refactored out

Second, we do the same with the form for adding new comments:

  1. Replace the <div id="new_comment"> for new comments with a call to another partial: <%= render :partial => 'comments/new' %>
  2. Move the code for the new comment unchanged into a new file called app/views/comments/_new.html.erb.

The view still looks the same in the browser, but we’re now posed to awe the world with our mad AJAX skills.

AJAX

Using AJAX is very easy, so I will just jump straight into it:

  1. Make the client loads the necessary Javascript files: Add the line <%= javascript_include_tag :defaults %> to the <head> section in app/views/layout/articles.html.erb. I bet you wondered where the title on the pages came from. Now you know.
  2. In app/views/comments/_new.html.erb, replace the call to form_for with an identical call to remote_form_for.
  3. In theory, this is all you need to do, and your form is now AJAX enabled, but we also have to specify what actions should be performed when the submit button is pushed. Right now, if you fill in a form and press submit, it will look as if nothing is happening. But if you refresh the page, you will see that the comment was indeed added to the database.
  4. Declare that creating a comment should support AJAX: In app/controllers/comments_controller.rb, find the method create and add format.js in the “if” block of method. Notice that no {} or anything is needed for this line (just like with most format.html calls in the other controller methods).
  5. Add the file app/views/comments/create.js.rjs. This will contain the code for what should be rendered when the user submits the form.
  6. RJS files are plain Ruby files, with a special object called page available. In app/views/comments/create.js.rjs, just enter a single line: page.insert_html :before, 'new_comment', :partial => "comments/comment"
  7. That’s it, if you post a comment, after some delay, it is displayed right before the new comment form.

Make it good

The AJAX now works, but it leaves a lot to be desired. Here are some improvements:

  1. Make the new comment stand out visually after it is created. Add the following to app/views/comments/create.js.rjs: page[dom_id(@comment)].visual_effect :highlight. (Or pick another effect from the script.acol.us webpage)
  2. Clear the form. Add the following to app/views/comments/create.js.rjs:
  3. Add a progress indicator: In app/views/comments/_new.html.erb, add the following line inside the “new_comment” div: <div id="progress" style="display: none">Saving...</div> and the :loading option to the call to remote_form_for: :loading => "Element.show('progress')".
  4. Even better, create a progress indicator at ajaxload.info. Download the image as public/images/ajax-loader.gif, and replace the contents of the progress div with <%= image_tag "ajax-loader.gif", :alt => "saving..." %>.
  5. Error handling doesn’t work. If you add some validation to the model, you will notice that the page behaves very poorly when the validation fails. Add the following line to app/models/comment.rb: validates_presence_of :title, :author, :body. If you try to submit a form that is missing one of these fields, the page will never update and the progress indicator will just keep going. To fix this, you can add the following line to the “else” block of the create method of app/controllers/comments_controller.rb: format.js { render :update do |page| page['new_comment'].replace :partial => 'new'; end }. This acts just as the RJS template from before, but without using a separate file (if you make this change, you will have to update your tests).
  6. You might also want to do something about the “flash”, but explaining that requires too much details, so I will leave that for another day.

AJAX goodness

We’ve covered the basics of AJAX: Using partials to make your views flexible, using remote_form_for to make the form submit asynchronously using JavaScript, and using RJS templates to generate JavaScript using Ruby.

There are many other options for using AJAX out there, and you might want to explore the field further. Ultimately, nothing beats JavaScript for JavaScript. That being said, built-in AJAX support in Rails is my favorite way to get started.

I hope you’ve enjoyed this tutorial. Some of the topics I am considering covering now are: Sessions, XML and RSS, development howtos like deployment, testing and application structure, or something completely different. Thanks a lot your for comments to my previous articles via the blog or email. Let me know what you want next.

Copyright © 2008 Johannes Brodwall. All Rights Reserved.

About Johannes Brodwall

Johannes is Principal Software Engineer in SopraSteria. In his spare time he likes to coach teams and developers on better coding, collaboration, planning and product understanding.
This entry was posted in Ruby-on-Rails. Bookmark the permalink.
  • http://www.abercrombiefitchzone.com abercrombie and fitch

    This post is very useful, I have learn much from this, thanks

  • Sreeragamratheesh

    nice one

  • Sreeragamratheesh

    good

  • Giulio

    Thanks for the great post, although I don’t quite understand how you do a few of the changes.
    for example: what do you mean by “find the method create and add format.js in the “if” block of method.” ?

    Could you please post the complete final code? That would help a lot.

    thanks

  • http://www.johannesbrodwall.com/ Johannes Brodwall

    Hi Giulio. I would recommend not looking too much at this post. Rails have progressed enormously in the last four years, and this information is out of date. :-)