Building a Better Drop Down Selection List for Ruby on Rails
Posted under LinkedIn, Ruby and Rails, on Saturday, December 10th, 2005;In building the View part (using RHTML Templates) of a RoR (Ruby on Rails) application we frequently want to present a controlled set or list of options for the user to select and way to do this is using the html select tag, which is easy enough to do in RoR, either building it with embedded Ruby (ERb) and/or using the select() helper method, and with only a little more effort you can do so using information stored in a database table via your models, of course, using a find() method to get the values. The “Agile Web Development with Rails” AWDR book has some excellent information on doing this (Chapter 17 pages 357 - 359 ).
The collection_select helper adds in some extra sugar automatation dealing with the collection.
<%=
@items = Item.find(:all, :order => "name")
collection_select("stuff", "item_id", @items, :id, :name )
%>
However, I find the current ways of doing this lacking in two ways:
- The element present to the users to select are based on the values of only one field. What if you what to be more descriptive and present more information?
- The use of the selected=”selected” attribute is necessary when you are editing an existing record - easy, but also for new elements there is no way to present a default element, unless the first element is the default. But what if you what to set Manitoba as the default in a list of provinces (M being in the middle), or present a blank choice or a “Please Select a Widget Type” message?
With this goal in mind I set out to build my more user friendly generated Select.
1 <select id="stuff_item_id" name="stuff[item_id]">
2 <% if @stuff.item_id == 0 or @stuff.item_id == nil -%>
3 <option value="" selected="selected" >Please Selected An Item</option>
4 <% end -%>
5 <% @items = Item.find(:all, :order => "name")
6 @items.each do |item| %>
7 <option value="<%= item.id %>"
8 <% if @stuff.item_id == item.id -%> selected="selected"<% end -%>
9 >
10 <%= item.name %> (<%= item.description %>)
11 </option>
12 <% end %>
13 </select>
which when used (when doing a “new”) I get something like this:
now for some explaining (assuming some knowledge of ruby and rails):
we are updating a stuff table/model with a field called “item_id”, there is also a table/model call item with fields “id”, “name”, and “description”
- line 1: start the select tag. the name=”stuff[item_id]” is important to populate the “item_id” field in “stuff”
- lines 2-4: checking the value in for the current record of stuff. When a new record is created it is zero, when it has failed validation it is “nil”. In either case this is where the default message (which might just be an emplty string), and value goes. You could also do a different default with validation fails.
- line 5: gets all the values from the Item model, sorted by name, and stores it in @items.
- line 6: start looping thru @items, one item at a time.
- line 7: builds each option tag of the select and sets the value attribute to “item.id”, which is what will be actually stored in “stuff”.
- line 8: sets the selected attribute if we are editing an existing “stuff” record.
- line 9: close the front part of the option tag.
- line 10: build what the user see, lin this case the name and in brackets the description
- line 11:then close the option tag for this value of item.
- line 12: close the looping for this item , which was started on line 6.
- line 13 :close the select tag.
Hopefully this is clear enough to help others. I’m still exploring the documentation and the RoR api doc has a section on ActionView::Helpers::FormOptionsHelper, with some specialized select Helpers.
I did not address the “Manitoba as the default in a list of provinces (M being in the middle)” objection in the above code. To do so you have have to A) know or figure out that “Manitoba” had a value of 6, then B) , my guess at best - or least worst - practice, do a if @stuff.item_id == 0 then @stuff.item_id = 6 to set this default value before the select.
Perhaps I should I will build a re-useable helper “collection_select_with_defaults”? …very very soon.
Amy Ho has some useful advice in A Little Form Help From Your Friends, all about Rail form helpers
I should also note that similar problems and solutions exist in PHP, JSP and ASP.
update: on the RoR wiki there is HowtoUseFormOptionHelpers
March 7, 2006 Update: , Sam Livingston-Gray not only linked here, but wraped it in a user *and* developer friendly helper function in the comments below
See also- Optimizing your RSS feed , minimizing your readers pain.
- Update on Lotus Formula Language > Developer tool: read/set document fields from view
- Robert Scoble is in in Kunal’s World now, (Now I know why we called them K-Logs) or How to build push button blogging.
- Lotus Notes News Reader v0.3
- SELECT @@IDENTITY


January 11th, 2006 at
Your example places ‘Please Selected An Item’ as the displayed value. How would you display one of the @items only knowing its array position?
January 11th, 2006 at
Line 8 answered my question.
February 3rd, 2006 at
Caution for dumbs like me! When you copy paste the code, the line numbers are included. So you have to delete them manually
Very well documented beautiful code… Thank you very much.
March 4th, 2006 at
any reason why you did not use:
options_from_collection_for_select(collection, value_method, text_method, selected_value = nil)
?
March 7th, 2006 at
[…] File under “note to self”: building a better dropdown selection list for Ruby on Rails. Next time I have a few minutes, I’ll stuff this into a helper function and send it back to the author. […]
March 7th, 2006 at
Found this yesterday via Google. I’ve been playing with Ruby and Rails for a week and a half, and wondered why there wasn’t any way to include a prompting value in a select tag. I’ve put together a tag helper that acts just like Rails’ “collection_select” method, but which takes an extra option called :first_row.
The following code assumes I have a Client model object, and that I’ve declared a method on it called “name_first_last_active” that shows a display-friendly name. (This fits well in a mixin called “Person,” by the way.)
I hope the following formatting comes through:
def polite_collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
# Get the HTML for everything in the collection as we ordinarily would
options_html = options_from_collection_for_select(collection, value_method, text_method, options[:selected_value])
# If we were given text for the first row, add another option tag with it
first_row = options[:first_row]
unless first_row.nil?
first_row_html = content_tag(:option, first_row, :value => ”)
options_html = “#{first_row_html}\n#{options_html}”
end
# Wrap the option tags in a select tag, giving it idiomatic id and name attributes
html_options[:id] = “#{object.to_s}_#{method.to_s}”
html_options[:name] = “#{object.to_s}[#{method.to_s}]”
select_html = content_tag(:select, options_html, html_options)
end
By putting this in a helper (like application_helper.rb), you can call this as a one-liner, just like the regular “collection_select” method:
“active DESC, first_name, last_name”) %>
polite_collection_select(:foo, :client_id, @clients, :id, :name_first_last_active, :first_row => ‘Please select a client:’) %>
Now it’s both user-friendly *and* developer-friendly. (= Hope this helps!
August 16th, 2006 at
like ocyrus, I think there is a simpler solution with the
ptions_from_collection_for_select(collection, value_method, text_method, selected_value = nil)
The new version would look something like:
1
2
3
This example assumes that you are passing in the @item parameter.
The “.to_i” may be required to get a proper match from the string parameter passed in, to an integer id type.
August 16th, 2006 at
That’s wierd, for some reason the post stripped the code lines after the number. Anyway I’ll try again wihtout line numbers, it should look lie
October 8th, 2007 at
Any development on your collection_select_with_defaults? Or could you refer any other site for this (ie, to set the default in a collection_select for RoR)?
Thanks.
January 5th, 2008 at
if we select one item in drop down then after updating the page when we see that there isn no item se4lected in drop down meny why tell me.
the code is set
April 24th, 2008 at
thank you for this. i tuned it for my application. not the easiest code to read but gets the job done.