Sortable Columns Across Tables

If you follow Railscast 228 you get sortable table columns for your model. But what if you don’t want to expose the name of the actual database columns or more interesting if you want to sort across tables? Here is how I do it.

In your controller add order or reorder if you already have an order clause in one of the used scopes (default_scope counts too).

class AttendeesController < ApplicationController
  def index
    @attendances = @conference.attendances.reorder(sort_query).page(params[:page])

  # ...

As I’m using this mechanism in different controllers I added the common functionality to the application_controller.rb file.

class ApplicationController < ActionController::Base
  helper_method :navigation_params, :sort_column, :sort_direction


  # supports only attendances for now
    "fee_payed" => "fee_payed",
    "fee_payed_on" => "fee_payed_on",
    "name" => "users.last_name, users.first_name",
    "payment_confirmation_sent_at" => "payment_confirmation_sent_at",
    "registered_at" => "registered_at",
    "registration_confirmation_sent_at" => "registration_confirmation_sent_at",
    "town" => ""

  # use this in views
  def sort_column
    ALLOWED_SORT_COLUMNS.keys.include?(params[:sort]) ? params[:sort] : "name"

  def sort_direction
    %w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"

  def navigation_params
    { direction: params[:direction], page: params[:page],  sort: params[:sort] }

  def sort_query
    sort_query_column.split(',').map{ |column| column + ' ' + sort_direction }.join(', ')

  # use this in controllers
  def sort_query_column

This will use the ALLOWED_SORT_COLUMNS hash to map between user visible and actual database sort columns. Adding sort_query also allows us to sort by multiple columns at once. navigation_params is a shortcut I use when generating URLs (e.g. in link_to) and I want to preserve pagination, sorting, filters/searches, etc. across pages.

  def link_to_sortable(column, title = nil)
    title ||= column.titleize
    sort_icon = column == sort_column ? content_tag(:i, nil, class: (sort_direction == "asc" ? "icon-chevron-down" : "icon-chevron-up")) : ""
    direction = column == sort_column && sort_direction == "asc" ? "desc" : "asc"
    link_to (title+" "+sort_icon).html_safe, params.merge(sort: column, direction: direction, page: nil)

Note that sort_icon assumes you are using Bootstrap.

Now we can have sortable columns in our views:

<%= link_to_sortable "name" %>

Leave a Reply

Your email address will not be published. Required fields are marked *