Previous Entry Share
How to sort filter drop-downs in ActiveAdmin
Universal #2
guypaddock
One of the frustrating things about how ActiveAdmin displays associations in filter drop-downs is that they're not sorted by default.

Let's say you're building a store on Rails that you can administer with ActiveAdmin. Assume you have an ActiveAdmin page that lists orders, and you have a filter that allows you to filter by customer. By default, the options in that filter might look something like this:

  • Frye, Adrienne A.

  • Doe, John

  • Adams, Ruth J.

  • Decaro, Felice L.

Ideally, you'd like it to look like this:

  • Adams, Ruth J.

  • Decaro, Felice L.

  • Doe, John

  • Frye, Adrienne A.

One way to do this is to set a default scope on User objects:

class User < ActiveRecord::Base

  ... your current User model code is here ...

  scope :ordered, -> { order('last_name', 'first_name') }
  default_scope { ordered }
end



However, be advised: This approach will cause all queries Rails generates that involve users to add a sort by last name and first name. This will affect query performance, and may cause confusion down the road when records aren't returned in the order you expect.

A better approach would be to extend ActiveAdmin to recognize when models have provided a scope that should be applied when they are rendered in drop-downs.

Try adding this to your ActiveAdmin initializer (i.e. the file where you have "ActiveAdmin.setup"):

ActiveAdmin.setup do |config|

  ... your current initialization code is here ...

  module Formtastic::Inputs::Base::Collections
    ##
    # Overrides {Formtastic::Inputs::Base::Collections.raw_collection} to
    # automatically sort options in filter drop-downs, enhancing the
    # usability of filter drop-downs in ActiveAdmin.
    #
    def raw_collection
      @raw_collection ||=
        (ordered_collection_from_options ||
         ordered_collection_from_association ||
         collection_for_boolean)
    end

    protected
      ##
      # Same as
      # {Formtastic::Inputs::Base::Collections.collection_from_options}
      # except that it automatically sorts the options, if they support
      # it.
      #
      # @return [Array]
      #   The sorted options.
      #
      def ordered_collection_from_options
        collection = collection_from_options

        if collection.respond_to?(:sort)
          collection.sort
        else
          collection
        end
      end

      ##
      # Same as
      # {Formtastic::Inputs::Base::Collections.collection_from_association}
      # except that it automatically applies a +filter_ordered+ scope to
      # the relation, if the relation supports it.
      #
      # @return [ActiveRecord::Relation]
      #   The relation for the collection.
      #
      def ordered_collection_from_association
        relation = collection_from_association

        if relation.klass.respond_to?(:filter_ordered)
          relation.filter_ordered
        else
          relation
        end
      end
  end
end



Now, if you want your users to come out sorted in filters throughout ActiveAdmin, you just need to declare a `filter_ordered` scope instead:

class User < ActiveRecord::Base

  ... your current User model code is here ...

  scope :filter_ordered, -> { order('last_name', 'first_name') }
end



This has the benefit that you don't need to tweak each and every filter through your ActiveAdmin resource pages, AND it has no effect on how users are queried in other contexts (i.e. you didn't have to declare a default scope).

?

Log in