qqbenq
qqbenq

Reputation: 10460

How to add a sortable table on ActiveAdmin's show page inside a tab?

Consider the following scenario: I have a resource with index and show pages in ActiveAdmin, and on the show page I have more Tabs.

example picture showing four tabs in resource show page

In some (possibly more than one) of those Tabs I would like to display information related to the resource in tabular form, with sortable columns. I would also require the application to stay on the selected Tab while changing sort order for the tables.

How to achieve this in ActiveAdmin?

Upvotes: 1

Views: 2094

Answers (2)

qqbenq
qqbenq

Reputation: 10460

Unfortunately ActiveAdmin doesn't seem to support this out-of-the-box, but it can be achieved.

in the ActiveAdmin.register call (usually in lib/admin/resource.rb) this would create a table for the resource's some_connection data:

tab "Tab 2" do
  table_for resource.some_connection do
    column :name
    ...
  end
end

Unfortunately this is not a sortable table, so we should simply add the following option hash:

{:sortable => true, :class => 'index_table'}

like so:

table_for resource.some_connection, {:sortable => true, :class => 'index_table'} do

Well, this will almost work. Unfortunately selecting a sort column will take you back to the first Tab, but the sorting will be stored in the URL (in whatever/your/page/address/is?order=name_asc format).

To solve this issue I monkey-patched ActiveAdmin's TableFor class with the following (in a new file I created in my project structure, e.g.: ext/active_admin/views/table_for):

module ActiveAdmin
  module Views
    class TableFor

      def build(obj, *attrs)
        options         = attrs.extract_options!
        @sortable       = options.delete(:sortable)
        @collection     = obj.respond_to?(:each) && !obj.is_a?(Hash) ? obj : [obj]
        @resource_class = options.delete(:i18n)
        @resource_class ||= @collection.klass if @collection.respond_to? :klass
        @columns        = []
        @row_class      = options.delete(:row_class) 
        @anchor         = options.delete(:anchor)
        @sort_key_prefix = ''
        @sort_key_prefix = @anchor.to_s + '_' unless @anchor.nil?

        build_table
        super(options)
        columns(*attrs)
      end

      def build_table_header(col)
        classes  = Arbre::HTML::ClassList.new

        sort_key = sortable? && col.sortable? && col.sort_key

        params   = request.query_parameters.except :page, :order, :commit, :format

        classes << 'sortable'                         if sort_key
        classes << "sorted-#{current_sort[1]}"        if sort_key && current_sort[0] == @sort_key_prefix + sort_key
        classes << col.html_class

        if sort_key
          th class: classes do
            link_to col.pretty_title, params: params, order: "#{@sort_key_prefix}#{sort_key}_#{order_for_sort_key(@sort_key_prefix + sort_key)}", anchor: @anchor
          end
        else
          th col.pretty_title, class: classes
        end
      end

    end
  end
end 

Basically I added an anchor parameter in options, and using this we can get back to the tab where the table is after selecting a sort criteria. Those criteria's name are extended with this anchor as a prefix, so having columns named the same in different tabs would not mess up the sorting of the different tables. (Currently I use the anchor as the prefix, but this could be changed to a fully separate option parameter to support more tables in one Tab)

I use an initializer to load the monkey patch, which simply requires the file which contains the code from above (ext/active_admin/views/table_for in my example):

monkey_patches.rb
-----------------
require 'ext/active_admin/views/table_for'

These changes require us to slightly modify the way we build the table (we have to retrieve the sort settings from the params and apply them to our data):

tab_name = "Tab 2"
tab tab_name do
  tab_anchor = tab_name.parameterize('_')

  data = resource.some_conection
  unless params['order'].nil?
    order = params['order'].to_s.sub(tab_anchor + '_', '').sub("_asc", " ASC").sub("_desc", " DESC")
    data = data.order(order) unless order.nil?
  end

  table_for data, { anchor: tab_anchor, :sortable => true, :class => 'index_table'} do
    column :name
    ...
  end
end

This is the result (here the table is actually put into a Panel, and the anchor points to that):

Example sortable table in Tab

Upvotes: 4

Kaka Ruto
Kaka Ruto

Reputation: 5125

Actually, ActiveAdmin does support this.

To sort the table, call the order() method and pass the column name and either desc/asc, like do:

table_for boat.bookings.order('created_at desc') do
  column :name
  column :created_at
end

Thanks to Ryan Bates' screencast: http://railscasts.com/episodes/284-active-admin?view=asciicast

Upvotes: -1

Related Questions