Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: Filter index page and Re-display filter inputs

I am not sure what the proper Rails 3 way to do this is. I want to have filter criteria fields at the top of my index view that will filter the data.

What I have below works for filtering my list, but I also want the filter fields to be re-populated with what the current filter is, and I can't figure out how to do that. The method I got working below also doesn't feel like the "right" way to accomplish this, since it relies on things like passing empty string to the field helper methods in my view.

Controller:

 def index

@animals = Animal.by_color(params[':color']).by_age(params[':age']).paginate(:page => params[:page])

respond_to do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @animals }
end

end

View:

<h1>Listing Animals</h1>

Filter By:
<%= form_for(:animal, :url => { :action => "index" }, :html => {:method => :get} ) do |f| %>
<div>
  Color: <%= text_field'', ':color', :size => 10 ) %>
  Age: <%= text_field('', ':age') %>
  <%= f.submit "Filter List" %>
</div>
<% end %>

<%= will_paginate %>

...

I am using the scope methods in my Model, which work very slick. I am just fuzzy about how the Controller "params" method and the View methods "form_for", "text_field" map to each other. Any ideas on how to refactor this and also get my filter fields populated when the list is currently filtered?

Solution!

Controller:

def index

@search = Animal.new(params[:animal])
@animals = Animal.by_color(@search.color)...

....

View:

<%= form_for(@search, :url => { :action => "index" }, :html => {:method => :get} ) do |f| %>
<%= f.text_field(:color)

The form population works by creating the @search object in my contorller (in addition to my main animals object). Then using form_for to create a for around it.

like image 247
Victor Bruno Avatar asked Aug 01 '11 19:08

Victor Bruno


1 Answers

What you want is a "query by example", which would work roughly like this in controller:

@filter = Animal.new(params[:filter])
@animals = @filter.get_scope.paginate(:page => params[:page])

and in the controller you could do a form_for on that example record, but I am not aware of any modern Rails plugins that do exactly that. There certainly should be something like this though, look thoroughly. I bet you could write something similar in a couple of hours though (just grab the attributes hash of your filter record into the :conditions of the scope, rougly like so - NOT TESTED THOUGH):

 def get_scope
   # remove all nill attrs
   non_default_attrs = self.attributes
   self.columns.each do | col |
     # Ignore columns that have default values
     non_default_attrs.delete(col.name) if non_default_attrs[col.name] == column.default
     # Ignore columns whose values are nils
     non_default_attrs.delete(col.name) if non_default_attrs[col.name].nil?
   end

   where(non_default_attrs.symbolize_keys)
 end
like image 128
Julik Avatar answered Sep 28 '22 00:09

Julik