Skip to main content

An Introduction to using AJAX with Rails: Take 2

Posted by bleonard on December 4, 2007 at 4:25 PM PST

In my first take on introducing AJAX with Rails, when a new comment is posted to an entry, the entire set of existing comments is replaced with a new set containing the new comment. Although the request is asynchronous, it's still inefficient, especially if the list of comments grows large. Ideally I would just insert the new comment to the bottom of the existing list, and that's what I show here.

Setting Things Up

I'm going to begin where the Building Relationships Between Rails Models tutorial leaves off. Alternatively, you can start from RubyWeblogComments.zip, which is the completed project from that tutorial.

Test the rubyweblog Project

  1. Open the rubyweblog project.
  2. Note, if you are starting with the provided rubyweblog project, you will also need to create the rubyweblog_development database and run the migrations.
  3. Run the project and browse to http://localhost:3000/blog to verify that it works.

The Plan

Currently, when a new comment is added, the entire page - blog entry and comments - are reloaded. Instead, we'll use AJAX to just insert the new comment to the bottom of the list. Just for fun, I will also show you how to apply a visual effect to highlight the most recently added comment.

Preparation

First we're going to move the fragment of rhtml that we want to dynamically insert, that being a single comment, into a partial template.

  1. Open show.rhtml


  2. Cut the following block of code from show.rhtml





  3. <%= h comment.comment %>

     

        Posted on <%= comment.created_at.strftime("%B %d, %Y at %I:%M %p") %>
     

  4.    


  5. Create a new RHTML file named _comment and place it in the app\views\blog folder.


  6. Paste in the code you cut above, replacing all existing content in _comment.rhtml.


  7. Return to show.rhtml and insert a call to the partial from where you cut the code. Also, place a
    tag with the id of "comments" inside the
      elements. We'll use this div to refer to this block of HTML when we insert the new comment. The new chunk of code in show.html should now look as follows:




         

            <% @post_comments.each do |comment| %>
              <%= render :partial=>"comment", :object => comment %>
            <% end %>
         



    • Test your changes. The application should behave as it did before.

Change the POST to an XMLHTTPRequest

  1. First, let's make sure the JavaScript libraries we're going to use are included in our application. Open blog.rhtml and add the following line below the stylesheet_link_tag:



    <%= javascript_include_tag :defaults  %>
    We're passing :defaults as the source because we're going to be using the Prototype and Scriptaculous libraries that come bundled with Rails.


  2. In show.rhtml, change the form_tag, which performs a HTTP POST, to a form_remote_tag, which performs an XMLHTTPRequest, as follows:



    <% form_remote_tag :url => {:action => "post_comment"} do %>

  3. Test. The entire page is no longer reloaded on submit. This is because submit is now performing an XMLHTTPRequest rather that a POST. As a matter of fact, it appears as though nothing has happened. However, if you refresh the browser at this point, you will see the comments are indeed being added.


  4. Open blog_controller.rb and navigate to the post_comment action.


  5. Replace the existing redirect_to method with the following render method:



    render :update do |page|
      page.insert_html :bottom, 'comments', :partial => 'comment'
      page[:comment_comment].clear
      flash.keep(:post_id)
    end



    The code above is dynamically inserting the _comment.rhtml partial into the bottom of the "comments"
    we defined above. It's important to keep the post_id in the flash, otherwise, any additional comments the user decides to insert will have a nil post_id, and will therefore be orphaned (there's no referential integrity defined on the comments table).



  6. Test and our comment should now be dynamically inserted into the bottom of the list.

Apply Visual Effects

The Scriptaculous library comes with a bunch of visual effects. I'm going to apply the highlight effect to the comment just posted. Once in place, you can easily swap in and try out any of the other effects.

  1. Like we did above for the entire comments section, we need to label the comment to which we want to apply the visual effect. We'll do this by adding a unique, deterministic id to each comment - what better than the comment id itself? Open _comment.rhtml and add the following id property to the
  2. element:



  3. >

  4. Then switch to blog_controller.rb and add the following to the block provided by render :update:



    page["comment_#{@comment.id}"].visual_effect :highlight, :duration => 3.5

  5. Test the end result:







    The highlight will appear for 3 1/2 seconds and then fade away.

Troubleshooting

Working with AJAX can be frustrating because when things go wrong you often don't get a notification in the browser (or if you do, it's a bunch of confusing JavaScript). In such cases I found the server output invaluable. For example, if you misspell the partial "comment" above when inserting the html, nothing happens when you click Post to add the comment. However, looking at the server's output shows the following helpful message:






 

The Completed Application

RubyWeblogAJAX.zip

Related Topics >>