By Stephen F. Booth
Download
Helper to sort tables or result sets using clickable column headers.
Most helpful when used in conjunction with the user-friendly PaginationHelper.
Add the following line to routes.rb:
map.connect ':controller/:action/:sort/:order'
The controller should look something like:
helper :sorting
def list
@sorter = SortingHelper::Sorter.new self, %w(icao host_id name), @params['sort'], @params['order'], 'icao', 'ASC'
@pages = Paginator.new self, Airport.count, 10, @params['page']
@airports = Airport.find_all nil, @sorter.to_sql, @pages.current.to_sql
render_action 'none' if 0 == @airports.length
end
And the view akin to:
<table>
<tr>
<th><%= link_to 'ICAO', @sorter.to_link('icao') %></th>
<th><%= link_to 'Host ID', @sorter.to_link('host_id') %></th>
<th><%= link_to 'Name', @sorter.to_link('name') %></th>
<th colspan="2">Location</th>
</tr>
<%= render_collection_of_partials "row", @airports %>
</table>
category:Helper
<tr class="centered data <%= row_counter.modulo(2) == 0 ? "row_light" : "row_dark" %>">
<td onclick="window.location.href = '<%= url_for :action => 'detail', :id => row.id %>'"><%=h row.icao %></td>
<td onclick="window.location.href = '<%= url_for :action => 'detail', :id => row.id %>'"><%=h row.host_id %></td>
<td onclick="window.location.href = '<%= url_for :action => 'detail', :id => row.id %>'"><%=h row.name %></td>
<td><a href="http://maps.google.com/maps?q=<%= row.wgs_dlat %>+<%= row.wgs_dlong %>&hl=en" target="_new"><%=h row.wgs_
lat %> <%=h row.wgs_long %></a></td>
<td><%= link_image_to 'details', { :action => 'detail', :id => row.id }, :title => 'Details' %></td>
</tr>
<%= link_to('< Previous', {:page => @pages.current.previous, :sort => params[:sort], :order => params[:order]}) if @pages.current.previous %>
<%= link_to('Next >', {:page => @pages.current.next, :sort => params[:sort], :order => params[:order]}) if @pages.current.next %>
{ :params => { ‘sort’ => column, ‘order’ => order }.merge(params) }
This is trying to do a “new.merge(old)” however it seems that the merge works the other way round in terms of ensuring that the new settings override the old. I tried the following and it seemed to work:
{ :params => params.merge({ ‘sort’ => column, ‘order’ => order }) }
Little improvement to propose: visualy identify the currently sorted column.
Add the following to methods to the Sorter class:
# if +column+ is the current selected order, return 'ASC' or 'DESC', else return nil
def column_order(column)
column = @default_sort unless @columns.include?(column)
if column == @sort
order = @order
else
order = nil
end
order
end
# little view helper to create the name of the class for the column
# by default return tablesort_none, tablesort_asc, tablesort_desc depending on whether column is ordered or not
def column_order_class(column, classname = 'tablesort', separator = '_', notordered='none')
order = column_order(column)
classname + separator + (order.nil? ? notordered : order.downcase)
end
Then in your view:
<tr>
<th></th>
<th align="left" class="<%= @sorter.column_order_class('name') %>"><%= link_to 'Email', @sorter.to_link('name') %></th>
<th align="left" class="<%= @sorter.column_order_class('short') %>"><%= link_to 'Short', @sorter.to_link('short') %></th>
</tr>
and in a stylesheet:
th.tablesort_none {
}
th.tablesort_asc {
background: url(/images/icon_sort_asc.gif) left center no-repeat;
padding-left: 16px;
}
th.tablesort_desc {
background: url(/images/icon_sort_desc.gif) left center no-repeat;
padding-left: 16px;
}