Rails Forms with Check Boxes
In my latest rails app, I want to compare a number of objects. My first solution was to create a new search page with a form on it.
<%= form_tag("/compare_ids", :method => 'get') do %> <%= text_area_tag(:z, nil, :size => "20x6") %> <%= submit_tag("Compare") %> <% end %>
The objects in question are plates, identified by a serial number. So, the user should type in the serial numbers (one per line) in the text area in the form. My compare_ids method then takes the data from the form (it comes in as a string) and finds each plate. The key is this method I have in the model. The method takes the string and breaks it up by the line breaks and then just cycles through to make a string to use in a find_by_sql command.
def self.get_plates(list) @ids = list.split(/\r\n/) @ids.each_with_index do |id, index| if(index == 0) @string = "(plates.serial_number = '#{id}')" else @string << " OR (plates.serial_number = '#{id}')" end end plates = find_by_sql("select plates.* from plates where (#{@string})") end
This worked, but I wasn’t that happy with it. Mainly because the serial numbers are kind of long and typing them in will lead to mistakes for sure.
I have another page where I list all our current plates along with their current location. I thought I should be able to make this page into a form where I put a checkbox in front of each plate. Then, the user just needs to check those to compare and hit the compare button. A much easier way of comparing a large number of plates.
My problem though was in setting up the form. I kept getting urls of the form z=123&z=456&z=789…, which lead to the value of z being the last one in the list. I wanted a way to get a string or array of values with all the values for z. Here’s how I solved it.
<%= form_tag("/compare_ids", :method => 'get') do %> <% @plates.each do |plate| %> <%= check_box_tag 'z[]', "#{plate.serial_number}"%> <%= link_to "#{plate.serial_number}", plate %> <% end %> <% end %>
The key here is the check_box_tag name is z[] and not just z. This will return an array. So my get_plates method above needs to be modified a little so that it can handle either a string or array. Here’s the final version.
def self.get_plates(list) # Given a string/array of incom serial numbers, get the info for those plates # The compare on plates#index returns an array, from advanced search it's a string if list.class == String @ids = list.split(/\r\n/) else @ids = list end @ids.each_with_index do |id, index| if(index == 0) @string = "(plates.serial_number = '#{id}')" else @string << " OR (plates.iserial_number = '#{id}')" end end plates = find_by_sql("select plates.* from plates where (#{@string})") end
It took me a while to figure this all out, but I’m really glad I did. My previous post on using ransack and check boxes is something I want to go back to because I think this is a much better way of handling things. I also have some more ransack stuff to do on this app and I think this will help.