Juba If you are interested, I have written a tutorial on how to paginate, sort and live search a table with Ajax and Rails. It is available at :
http://dev.nozav.org/rails_ajax_table.html
Disclaimer: This might be the worst way ever to solve this requirement, but I wrote this in 10 minutes and I’ve never looked back!
My plan here was to have a list of ‘items’ in my admin interface that the user can paginate through and select to edit.. using the joys of rails 0.11.x, pagination, ajax, and liberal helpings of vaseline.
The only 2 things of note here are the use of render_partial in this action, and the paginate call to set up my list of ‘items’ for the user to browse through. Read the Rails API docs on these two to get savvy, you savvy?
model :item
...
# in the 'catalogue', when you click on a section, you list all the items for it.
# this is the paginated ajax snippet
def item_list_with_ajax
unless @params['id'].nil?
@item_pages, @items = paginate :items, :conditions => ["section_id = ?", @params['id']], :order_by => "name DESC", :per_page => 20
end
render_partial 'admin/blocks/item_nav'
end
For fun, I’ve included my entire partial, showing you the love. The only thing unusual is this pagination_links_with_ajax method. Where did that come from, I wonder? Life’s full of mystery, I love it. I love gravy, too.
Item Browser
<div id="item-sidebar">
<% unless @items.nil? or @items.empty? %>
<% for item in @items %>
<div class="little-black-text">·
<%= link_to_remote item.name, :update => 'content-body', :url => { :action => 'item_edit_with_ajax', :id => item.id } %>
</div>
<% end %>
<hr/>
<div class="tipster"><%= link_to_remote 'Refresh', :update => 'item-sidebar', :url => { :action => 'item_list_with_ajax', :page => @params['page'] } %>
· <%= pagination_links_with_ajax @item_pages, { :element_name => 'item-sidebar', :action => 'item_list_with_ajax' } %></div>
<% else %>
<div class="little-grey-text">no items</div>
<% end %>
</div>
Aha! The final piece of the puzzle – it’s like finding the holy grail to aid your dying father (who was shot in the stomach and perhaps the spine by a villainous antiquarian with dreams of glory and everlasting life):
module AdminHelper
# ripped straight out of docs, modified for ajax
def pagination_links_with_ajax(paginator, options={})
options.merge!(ActionView::Helpers::PaginationHelper::DEFAULT_OPTIONS) {|key, old, new| old}
window_pages = paginator.current.window(options[:window_size]).pages
return if window_pages.length <= 1 unless
options[:link_to_current_page]
first, last = paginator.first, paginator.last
returning html = '' do
if options[:always_show_anchors] and not window_pages[0].first?
#html << link_to(first.number, options[:name] => first)
html << link_to_remote(first.number, :update => options[:element_name], :url => { :action => options[:action], :page => first })
html << ' ... ' if window_pages[0].number - first.number > 1
html << ' '
end
window_pages.each do |page|
if paginator.current == page && !options[:link_to_current_page]
html << page.number.to_s
else
#html << link_to(page.number, options[:name] => page)
html << link_to_remote(page.number, :update => options[:element_name], :url => { :action => options[:action], :page => page })
end
html << ' '
end
if options[:always_show_anchors] && !window_pages.last.last?
html << ' ... ' if last.number - window_pages[-1].number > 1
#html << link_to(paginator.last.number, options[:name] => last)
html << link_to_remote(last.number, :update => options[:element_name], :url => { :action => options[:action], :page => last })
end
end
end
end
You can even see where I commented out the original link_to method calls and snuck in link_to_remote. Aren’t I devious? Almost like picking the wrong cup of a carpenter.
So, there you have it, folks, ajaxified lists for you heal the wounded web-paradigm with everlasting, asynchronous pagination!
But don’t cross the seal.
—ScottMaclure
Or another way that is probably more robust to internal changes in paginate_links
Add to application_helper.rb
# Identical to +pagination_links+ except uses +link_to_remote+ instead of
# +link_to+. Additional options are:
# +:update+:: same as in +link_to_remote+
def pagination_links_remote(paginator, options={})
update = options.delete(:update)
str = pagination_links(paginator, options)
if str != nil
str.gsub(/href="([^"]*)"/) do
url = $1
"href=\"#\" onclick=\"new Ajax.Updater('" + update + "', '" + url +
"', {asynchronous:true, evalScripts:true}); return false;\""
end
end
end
Without Ajax:
pagination_links(@item_pages, :window_size => 5)
With Ajax:
pagination_links_remote(@item_pages, :window_size => 5, :update => "item-sidebar")
:update, :complete, :before, :success, etc.
Add to application_helper.rb
module ActionView
module Helpers
module PaginationHelper
def pagination_links_remote(paginator, page_options={}, ajax_options={}, html_options={})
name = page_options[:name] || DEFAULT_OPTIONS[:name]
params = (page_options[:params] || DEFAULT_OPTIONS[:params]).clone
pagination_links_each(paginator, page_options) do |n|
params[name] = n
ajax_options[:url] = params
link_to_remote(n.to_s, ajax_options, html_options)
end
end
end # PaginationHelper
end # Helpers
end # ActionView
With Ajax:
<%= pagination_links_remote @user_pages, {:window_size => 2},
{:update => 'UserList',
:complete => visual_effect(:blind_down, 'UserList'),
:before => %(Element.show('spinner')),
:success => %(Element.hide('spinner'))} %>
—Mike Gaffney
def pagination_links_remote(paginator, page_options={}, ajax_options={}, html_options={})
pagination_links_each(paginator, page_options) {|page|
ajax_options[:url].merge!({:page => page})
link_to_remote(page, ajax_options, html_options)}
end
pagination_links_remote(@user_pages, {:window_size => 3},
:url => {:action => :list})