Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creating Custom Filters #45

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

Creating Custom Filters #45

tylermcgill opened this issue May 17, 2011 · 11 comments

Comments

@tylermcgill
Copy link

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?

@torrick
Copy link

torrick commented May 17, 2011

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
Copy link
Author

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.

@torrick
Copy link

torrick commented May 18, 2011

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
Copy link
Author

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.

@torrick
Copy link

torrick commented May 19, 2011

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.

@gregbell
Copy link
Contributor

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
Copy link
Author

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
Copy link
Contributor

rdj commented Jul 1, 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
Copy link
Contributor

rdj commented Jul 1, 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
Copy link
Author

@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?

@gregbell
Copy link
Contributor

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
Projects
None yet
Development

No branches or pull requests

4 participants