If you have ever used the select_tag in Rails, you migth have encountered a situation where you needed an blank option as the first option in your select list, only to discover that it was not supported. How ever, usign the options_for_select you can fix this even when what you really wanted to use, was the options_from_collection_for_select… you have to love those method names :)Fixing this isn’t hard, all that is required is the knowledge of ruby’s array methods and how to change an active record collection into an array.
Here is the finished solution, nice and simple:
<%= select_tag 'project[category_ids][]',
options_for_select(Category.all.collect
{ |c| [c.name, c.id] }.insert(0, '')) %>
- and in the true spirit of the Rails API I have even used a project management app as reference :) As you can see, this is a simple has_and_belongs_to_many association, but you can use this any way you want.
Lets go through the code
By firing up our script/console we can test the code above to get a clearer understanding of what it does. In my case, a Category.all will return something like this:
>> Category.all => [#<Category id: 2, name: "Requirement Gathering", created_at: "2009-06-02 18:49:56", updated_at: "2009-06-02 18:49:56">, #<Category id: 3, name: "Development Life cycle", created_at: "2009-06-02 18:57:43", updated_at: "2009-06-02 18:57:43">, #<Category id: 4, name: "Customer Review & Feedback", created_at: "2009-06-02 18:59:22", updated_at: "2009-06-02 18:59:22">]
and as you can see using the options_from_collection_for_select I could have written the following:
Category.all, :id, :name
But I need an empty option as the first option in my select list, so using the code above will not work, but by using the option_for_select, which takes an array I can insert an element into the array given it’s index like this:
['Rails', 'PHP', 'JavaScript'].insert(0, "")
Let’s try this in the console and we will get ["", "Rails", "PHP", "JavaScript"] have an empty element with index 0, and the collect method Invokes block once for each element of self. Creates a new array containing the values returned by the block so by using Category.all.collect { |c| [c.id, c.name] } we will get an array build the way the options_for_select likes it, and since the return from that block is an array, we can call the insert method on it and adding the empty options as the first element. We can verify this with the following snippet in our console:
>> Category.all.collect { |c| [c.id, c.name] }.class
=> Array
And by calling the insert method on that snippet we will get:
>> Category.all.collect { |c| [c.id, c.name] }.insert(0,"")
=> ["", [2, "Cat#2"], [3, "Cat#3"], [4, "Cat#4"]]
>>
which will give us what we want; an empty element as the first option in our select_tag.
Enjoy!
Exactly what I was looking for, thanks Kristian !
Much obliged, a very nice solution, now retrofitting to some custom helpers.
Very smart!
Just ran into this as well. Above worked like a charm, thanks for posting.
options_from_collection_for_select returns a string of option tags, so just specify a blank option tag like this…
“–not specified–” + options_from_collection_for_select(@duhs, :id, :name, @duh))
oops, it chewed up my option tags — well you get the idea.
Thanks so much for taking the time to write this up! I have just used it successfully :)
An alternative can be to add {:include_blank => true} to dispay blank option, which is given by Rails select FormHelper or {:include_blank => “– Select One –”} to display “– Select One –” instead of blank option.
Thanks a lot! Very useful!
Note: it can also be used for the missing :prompt option in the select_tag.
Pingback: Rails options_for_select with include blank and selected option « Kristian Nissen
Thank u