Easy Form Drop-Downs With Collection_Select
It’s a pretty common thing in Ruby on Rails–or even in web development–that you have some set of objects that you want to list in a drop-down. Writing this code in raw HTML is tedious–you need to have a select tag, and multiple option tags in it.
First, let’s review some sample HTML code to help clarify. Say you have a list of customers, each with an ID and a name, and you want to list that. Each sales_agent has one client–a customer instance. The HTML code would look something like this:
<select name="sales_agent[customer_id]" id="sales_agent_customer_id">
<option value="1">Jack Straw</option>
<option value="6">Ahmed Haqq</option>
<option value="13">Dima Petriovich</option>
</select>
Notice the name and ID for the select tag, as per the Ruby on Rails conventions for mapping model objects. Option tags have a value and a display string.
So how can we do this more easily in Ruby on Rails?
Fortunately, for models, Ruby on Rails has a simple and convenient helper-method to help you do this quicker–collection_select. The API is:
collection_select("object_name", "field_name", list_of_objects, "value_field", "display_field")
For the above code, our API call might be something like this:
collection_select("sales_agent", "customer_id", @customers, "id", "name")
Assuming you had a customer model with an ID attribute and a name attribute, this would produce exactly the above code. So looking at the values we pass into the collection_select call:
- The first parameter is the model that contains one element from the collection (eg. sales_agent)
- Next is the field name in the model that refers to the collection element it contains (eg. customer_id)
- Next is the variable containing the collection of items that we want to list (eg. @customers)
- Next is the value attribute of the option tag (eg. the customer id)
- Next is the display attribute of the option tag (eg. the customer name)
One final note: When you use this–in your views–you’ll need to select the collection first in your controller. Most likely you’ll use something like @customers = Customer.find(:all, :conditions => "owner_id = 3") or something similar to fetch the collection you want to display.
And that’s it! Tedious HTML selection fields become as simple as one collections_select call–providing you follow all the Rails conventions, and have models in your application domain.
Tags: collections, helpers Posted in



April 28th, 2009 at 8:11 pm
Okay, great, but this doesn’t explain at all how to use it. How do you get the selected option when you are in the controller? Rails documentation is significantly lacking in every aspect.
April 29th, 2009 at 5:48 am
@Justin: If you look at the original HTML, you’ll see: name=”sales_agent[customer_id]“. The Rails code generates equivalent HTML; it already follows the convention Rails knows, so it should automatically populate sales_agent[customer_id].
May 22nd, 2009 at 11:57 pm
The current signature is the following:
collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
Sample usage (selecting the associated Author for an instance of Post, @post):
collection_select(:post, :author_id, Author.find(:all), :id, :name_with_initial, {:prompt => true})
Thanks for the article.
June 23rd, 2009 at 10:51 am
On this helper, what would be the best way to select a default?
example
searcher.get_approver}) %>
This throws an error
Syntax error, unexpected ‘{’, expecting ‘)’
… @approvers, :id, :full_name {:selected => searcher.get_appr…
I would also like an option for : “Create New Approver” which links to Approver.new
Any ideas would be appreciated.
On a secondary question;
I have on the same form, a set of radio-buttons. I would like the toggle to enable or disable this collection_select control
Thanks
June 23rd, 2009 at 10:52 am
searcher.get_approver}) %>
July 29th, 2009 at 5:06 am
Hi,
I have a question:
What if “display_field” is a virtual attribute which contains parameters?
In your example, assume that to calculate the “name” attribute, we need to pass a parameter “language”. So how can we use the collection_select in this case?
Thank you in advance.
July 29th, 2009 at 12:09 pm
@Brian: sorry, no clue
@Trung: I’m not sure. I assume if you put a function name, Rails will call the function and use the return value as the attribute. Why not try it, and let me know how it works out?
July 29th, 2009 at 8:40 pm
@ashes999: Thanks for your reply. I have tried with collection_select but no progress. So I have found another solution by using select. For examples:
select(”sales_agent”, “customer_id”, @customers.collect{|customer| [customer.name(params), id]})
With this, you can call any instance function with params.
Hope this help for somebody.
@Bryan: Have you tried to put “,” before {:selected => …} in your code?
Thanks and regards.