Skip to content
This repository

Creating Custom Filters #45

Closed
tylermcgill opened this Issue May 17, 2011 · 11 comments

4 participants

tylermcgill Greg Bell Thomas rdj
tylermcgill

I'm trying to create a custom filter to no avail. I need to be able to take a numeric search and check it against multiple rows, the exact number of rows being another input for the filter. Is there anyway to create a custom filter like a custom view?

Thomas

I think what you're looking for is the "scope" feature.

In your model create a regular named scope:

scope :mynamedscope where('somefield > 10')

Now go to where the model is registered with ActiveAdmin (app/admin/yourmodelname.rb)

scope :mynamedscope

Take a look at the demo project. There are a few scopes setup for several models and you can see how it is setup.

activeadmin-store repository
demo site

tylermcgill

Thanks for the reply! You're correct - I am looking at using a complicated scope. However it is a dynamic scope - and the behavior I'm looking for is more like a filter. I want the user to be able to adjust two values which are then passed to my dynamic scope which then updates the displayed set of records.

Please let me know if you need more detail and I'll work up a code snippet.

Thomas

A code snippet would help. But my first thought is to combine an ActiveAdmin scope and configure the filter form to refine the rows based on user input.

tylermcgill

Could you explain what you mean by "configure the filter form to refine rows based on user input"? My ideal would be the option to create a custom filter where 1) I specify the fields needed, 2) I create a block that is passed the field values and 3) I return either a AREL or the actual set (running my one queries / filtering thereon). If there is a better way, please let me know.

For reference, here's my search method (I've left out class details as I think the variable usage in the scope is evident as is):

def self.search(attendance_level, options = {})
    options[:num_courses] ||= 1
    options[:direction] ||= ">"
    joins(:memberships => [{:course => :semester}])
    .where("memberships.attendance #{options[:direction]} ?", attendance_level)
    .group(:person_id)
    .having("count(memberships.id) > #{options[:num_courses] - 1}")
    .merge(Semester.active)
  end

I want the user to be able to enter the variable values in as a filter and see the resulting set.

Thomas

You can set a filter for a particular model by using the filter method:

#app/admin/yourmodel.rb
ActiveAdmin.register YourModel do
  filter :somecolumn
...
end

Now this is static filter and the fields are predefined. I haven't figured out a way to get rid of the filter form thats included on every index automatically so you might as well use it (that was my thought process anyway). If you're trying to get this form on the index action (which I assume you're doing right?) you may run into trouble. I don't think the DSL supports that just yet judging by where you define the form currently.

Greg Bell
Owner

The filters use meta_search (https://github.com/ernie/meta_search) to filter your collection. Anything that meta_search supports (check out the readme), the Active Admin filters can support.

Check out the ActiveAdmin::FilterFormHelper if you would like to add a method for a new filter type:
https://github.com/gregbell/active_admin/blob/master/lib/active_admin/view_helpers/filter_form_helper.rb

tylermcgill

Just a brief update: I haven't died - I've been researching how meta_where works and how to include it. I'll report back with the details when finished.

rdj
rdj commented June 30, 2011

Here's a simple example I threw together that creates a new filter type :custom where the attribute is just the appropriate meta_search param.

https://gist.github.com/1057991

@gregbell - I noticed the [].join.html_safe pattern throughout active_admin. I'm not familiar with what escaping Arbre does, but you may want to look into using the safe_join helper instead, since join.html_safe would not properly escape unsafe strings. See my gist for an example.

rdj
rdj commented July 01, 2011

@fractur3d - If you want to get rid of the filter sidebar, just clear the sidebars like this:

ActiveAdmin.register Resource do
  controller.clear_sidebar_sections!
end
tylermcgill

@rdj - thanks for the example!

My challenge is that I'm trying to use a custom search method that requires multiple parameters. MetaWhere does support this (see https://github.com/ernie/meta_search and search for the heading "Accessing custom search methods (and named scopes!)") but I had great difficulty in figuring out how to integrate into activeadmin. Here's what I've put together at the moment:

# app/admin/people.rb
ActiveAdmin.register Person do
  filter :retention_search_mw do
    render :partial => "retention_search_form"
  end
end
# app/views/admin/people/_retention_search_form.html.haml
- form_for :person, :html => {:method => :get} do |f|
  .filter_form_field.filter_numeric
    = label_tag "q[retention_search_mw(2)]", "All students with attendance"
    = select_tag "q[retention_search_mw(2)]", options_for_select([["Above", ">"], ["Below", "<"]])
    -# label_tag "q[retention_search_mw(1)]", "Attendance Level"
    = text_field_tag "q[retention_search_mw(1)]", nil, :size => 10
    %span %
  .filter_form_field.filter_numeric
    -# label_tag "q[retention_search_mw(3)]", "In at least"
    %span &nbsp;&nbsp; in at least
    = text_field_tag "q[retention_search_mw(3)]", nil, :size => 10
    %span course(s)
  = f.submit "Filter"
  = link_to("Clear Filters", "#", :class => "clear_filters_btn")
# app/models/person.rb
class Person < ActiveRecord::Base
  search_methods :retention_search_mw, :splat_param => true, :type => [:integer, :string, :integer]

  def self.retention_search_mw(attendance, direction, num_courses)
    return scoped unless attendance
    direction = ">" if direction.length == 0
    num_courses ||= 1
    Person.retention_search attendance, {:direction => direction, :num_courses => num_courses}
  end

  def self.retention_search(attendance_level, options = {})
    options[:direction] ||= ">"
    options[:num_courses] ||= 1
    joins(:memberships => [{:course => :semester}])
    .where("memberships.attendance #{options[:direction]} ?", attendance_level)
    .group(:person_id)
    .having("count(memberships.id) > #{options[:num_courses] - 1}")
    .merge(Semester.active)
  end

It works, but it feels hackish and it isn't part of the "Filter" sidebar. In addition, the values don't persist in the form fields from request to response.

Any ideas on how to better integrate this?

Greg Bell gregbell closed this July 18, 2011
Greg Bell
Owner

If you would like to add a new filter, add it to ActiveAdmin::ViewHelpers:: FilterFormBuilder available. Take a look at filter_form_helper.rb to see how they're implemented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.