Tag Archives: rails

Super-simple ajaxy pagination in rails with jQuery and WillPaginate

I found myself needing to do some pretty heavy ajax for a recent project, and I needed to ajax pagination with an added twist of user defined per_page.

I looked around the web and found a bunch of ideas, but most of them were way more complex than necessary.

I was already using jQuery for my javascript library and will-paginate for pagination, so I went down the simplest route I could think of.

Firstly, I am assuming that you have a project using jquery and will_paginate, and they are installed properly.

Then, let’s say you have an index view that renders a list of products through a partial.

File: products/index.html.erb

<h1>Listing products</h1>

<div id="products-container">
  <%= render :partial => "products" %>
</div>

File: products/_products.html.erb

<table>
  <tr>
    <th>Name</th>
    <th>Description</th>
    <th>Price</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
  <% @products.each do |product| %>
    <tr>
      <td><%= product.name %></td>
      <td><%= product.description %></td>
      <td><%= product.price %></td>
      <td><%= link_to 'Show', product %></td>
      <td><%= link_to 'Edit', edit_product_path(product) %></td>
      <td><%= link_to 'Destroy', product, :confirm => 'Are you sure?', :method => :delete %></td>
    </tr>
  <% end %>
</table>

First let’s handle the user-defined per_page attribute.

In your _products.html.erb file, add to the end:

<%= select_tag :per_page, options_for_select([10,20,50], (params[:per_page].blank? ? 20 :  params[:per_page].to_i)) %>
<%= will_paginate @products, :id => "pagination", :previous_label => "Previous", :next_label => "Next" %>

The pagination must go in the partial so that it updates when the ajax response is rendered.

Then add the javascript to index.html.rb:

<script type="text/javascript" charset="utf-8">
  <% content_for :js do %>
    // This ajaxSetup is necessary for rails recognizing format.js. Done in application.js preferably.
    jQuery.ajaxSetup({
      'beforeSend': function(xhr) {
      xhr.setRequestHeader("Accept", "text/javascript")
    }
    });

    // channel per page submit on change
    $('#per_page').live('change', function() {
      $.get(<%= products_path() %>, { per_page: $('#per_page').val()}, function(data) {
        $('#products-container').html(data);
      });
    });
  <% end %>
</script>

This assumes you have a yield :js set up in your application.html.erb file.

Now in your products_controller index action add some code to handle pagination and return something for the format.js like this:

def index
  @per_page = params[:per_page].blank? ? 20 : params[:per_page]
  @products = Product.all.paginate(:per_page => @per_page, :page => params[:page] ? params[:page] : 1)

  respond_to do |format|
    format.html # index.html.erb
    format.js { render :partial => "products" }
  end
end

At this point, if you load up the products page, you should be able to select the desired per_page number and the page should update to reflect the new pagination. However, if you click on any page, you’ll see that the page still refreshes. Let’s handle that now.

Add this javascript to index.html.erb file:

  // handle pagination links as ajax
  $('.pagination a').live('click', function(){
    $("#products-container").load( $(this).attr('href') );
    return false;
  });

That’s it. Now the page should update via ajax all smooth-like. It’s a little hacky in how I’m handling format.js to just return a partial, but in my case I have no other use for the js handler. Let me know if it works for you or if you have any questions.

Tagged , , , , , , ,
Follow

Get every new post delivered to your Inbox.