Select helper methods in Ruby on Rails
May 01, 2007 14:41
It can get quite confusing when it comes to using the different select helpers, select, select_tag and collection_select and in my opinion there is a lack of adequate documentation.
For my own use if nothing more, i have done my best to compare the different form helper methods available, and when and how to use them.
First we will look at the html for a basic selection box:
<select name="payment"> <option value="1">VISA</option> <option value="2">MasterCard</option> <option value="3">Switch</option> </select>
The selection box has some key parts, the name, which is required, and used by the browser when submitting the <select> choices to the server.
The option tags, each of which are made up from a "value" and "text" pair, the "value" to identify the select item in the server, and the "text" which will be displayed to the user.
There are three different select form helpers in ruby on rails, "Select", "select_tag" and "collection_select". We will have a closer look at each of them now.
select
select(object, method, choices, options = {}, html_options = {})Defined in ActionView::Helpers::FormOptionsHelper
Use select when you require a basic drop-down selection box populated with data not sourced from a database.
- The object is the name of an instance variable. This is typically a model object (singular name of the table whose data your displaying, or in other words, the table record).
- The method is the attribute of that instance variable. This is typically a field/column of the table whose data your displaying (really an ActiveRecord method).
- Together the object and method specify the name of the select statement in the generated html
- choices can be any enumerable object e.g arrays and hashes and results of database queries, and contains the option tags for the select box.
- The optional options argument takes various "options" some of which are listed below in the examples.
- The optional html_options argument allows css to be used for styling the select box.
- If one of the option tags in choices matches @object.method, that option tag will be selected
In this example a hash is being used to populate the option tags.
<%= select( "payment", "id", { "Visa" => "1", "Mastercard" => "2"}) %>
Select can be used in conjunction with a model object as seen in this example, an instance variable is passed into choices, but is being converted into an array of arrays.
<%= select ("selected_payment", "id", @payments.map {|u| [u.name,u.id]}) %>
The controller may look something like this:
@payments = Payment.find(:all) @selected_payment = @payments[2]
This will cause the third option_tag to be selected by default in the select box, but collection_select is preferable in this scenario.
select_tag
select_tag(name, option_tags = nil, options = {})Defined in ActionView::Helpers::FormTagHelper
Use select_tag when you require a drop-down selection box populated with data not sourced from a database, and are happy to hard code the default selected option tag. Select_tag should also be used when you want to process your form as a GET, rather than a POST, see here for more details. Also note that select_tag does not have a html_options parameter
- The name specifies the name of the select statement in the generated html, and is used by the controller to access the selected items.
- The optional option_tags argument is a string containing the option tags for the select box.
- The optional options argument takes various "options" some of which are listed below in the examples.
Simple use of the select_tag helper:
<%= select_tag "payment", "<option>VISA</option>" %>
In this example, we are using options_for_select which accepts any container and returns a string of option tags, but we have defined an array. Note how the optional second parameter specifies the selected item in the list.
<%= select_tag "payment", options_for_select([ "VISA", "MasterCard", "Switch" ], "MasterCard") %>
Another option is this
<%= select_tag "payment", options_for_select(%w{ VISA Mastercard Switch }) %>
where %w is the shortcut notation for an array of strings that are space separated.
You can also do multi-select boxes:
<%= select_tag 'payment[]', options_for_select(@payments), :multiple => true, :size => 3 %>
Note that the "name" in this case is an array, and @payments is a hash defined in the controller:
@payments = {'Visa' => 1, 'Mastercard' => 2, 'Switch' => 3}
We can access the selected payment methods in the controller, using the array:
params[:payment]
select_tag does not support the :prompt option, which may not seem like an issue as we have already shown above how you can select a default option tag. But what if you want your form to GET, not POST, your only option is then to use select_tag, even if your data source is a database.
<%= select_tag(:payment, "<option>-Select a payment method</option>" + options_from_collection_for_select(@payments, :name, :name)) %>
As select_tag takes a string, we can concatenate an additional string onto the beginning of the parameter, this allows us to add our default option_tag. We use options_from_collection_for_select which takes a value_method, and text_method (see collection_select for their definition) and an optional selected_value, and a collection, which can be defined in your controller like this:
@payments = Payment.find(:all, :order=>"name")
collection_select
collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})Defined in ActionView::Helpers::FormOptionsHelper
Use collection_select when you require a drop-down selection box, whose source is a model/object
- The object is the singular name of the table whose data your displaying (the table record).
- The method is the field/column of the the relevant data (really an ActiveRecord method).
- Together the object and method specify the name of the select statement in the generated html
- collection takes the option tags for the select box, this can be a hash or array.
- value_method is the field/column to use for the value of the option tags in your html.
- text_method is the field/column to use for the visible text of the option tags in your html.
- The optional options argument takes various "options" some of which are listed below in the examples.
- The optional html_options argument allows css to be used for styling the select box.
- If one of the option tags in collection matches @object.method, that option tag will be selected
In this example the payment methods are being added to the select box via a model object, notice how we use the option, :prompt to add an additional option tag to the select box, which will be selected by default. Note that if @object.method matches one of the option tags, this will be selected by default, and :prompt wont appear in the list.
<%= collection_select(:payment, :id, @payments, :id, :name, options ={:prompt => "-Select a payment"}, :class =>"payment") %>
The selected item can be accessed in the controller, by using:
selected_payment = params[payment][id]
A final Note to say that the parameters used in all the helpers may be strings or symbols, symbols may be a better choice in some situations, but thats another article.
Comments
Finally! some decent documentation of select - thanks very much; perhaps you can contribute this back to rails doc project.
"The optional options argument takes various "options" some of which are listed below in the examples."
No, no they are not, in fact, listed below in the select() examples or even mentioned again at all...
What if you need to create a SELECT for a group of items that aren't directly related to the object? Which SELECT help do you choose?
For instance, if I'm creating a user and I want a drop-box for "Number Of Times To Duplicate User", how do I create a drop-box that will pass values back?
finally i got the "selected" for the collection_select helper, thanks a lot!! i only think that it would be clearer for newbies like me if you enforce the fact that the helper will look for the @payment variable and call it's id method to perform the comparison with the array @payments and generate the selected label.
Hi,
Thanks,
But how can I select value by default or onn some event? ... or what would be the selectedIndex
>> The way I do it is using RJS, to swap out the entire selection box with a new one that contains the updated data. If there is enough interest in this I could write a tutorial? let me know what you think.
I think that tutorial would be helpful to a lot of people, not to mention interesting. Please go ahead with the tutorial. I know I'm interested to read that one. ;)
I want to create a selection box with two choices, Hat lover and hat designer. Can anyone explain to me why this select tag isn't working:
<%= select(:user_profile, :type, {"Hat lover", "Hat Designer"}) %>
I get this error:
TypeError in Community#edit_profile
Showing app/views/community/edit_profile.rhtml where line #21 raised:
wrong argument type String (expected Module)
Extracted source (around line #21):
18: <%= text_field :user_profile, :location, :size => 30 %>
19:
20:
21: <%= select(:user_profile, :type, {"Hat lover", "Hat Designer"}) %>
22:
23:
24: <%= text_field :user_profile, :owned_hats, :size => 10 %>
Was wondering if anyone knows why the collection_select with multiple html_options adds '---' to the beginning of any entry for any selection made in the multiple select box..
basically this dropdown finds these 'indicator' strings in another table, and assigns it to another string in a different table. However, if I select anything from the drop down, it adds '---' to the beginning of each entry :(
<%= collection_select(:datab, :indicator, Indicator.find(:all, :order => 'indicator'), :indicator, :indicator, {}, html_options = {:multiple => true, :size => 5}) %>
Can anyone explain me how to make an AJAX call in rails on selection of a list item? Thanks in advance
Hey,
Thanks for this. It would be really helpful if you showed how the select was used with a form, especially for the GET scenario as I am trying to do this and finding select with how to use the form in one place would be nice,
Cheers
Thank you so much for posting the explanations about select, select_tag, and collection_select! It has made things much more clear. There is indeed a lack of "plain English" documentation which is odd for a language which boosts to be and is very readable.
Thanks for this post. It's much clearer than the real documentation because you've contrasted the different methods and their different uses.
Thanks a lot! This article was extremely helpful and saved me hours of frustration.
Thanks for the tutorial.
I am having an issue trying to get the drop downs to work with rails 1.2.6 .
I have implemented:
<%= collection_select(:status, :id , @statuses, :id, :status) %>
in _form.rhtml, the intention being to simply list the relevant status codes from the DB. In the controller I have implemented a find_all so that the @statuses variable is set. When I invoke the action I get:
NoMethodError in Propagationrecords#new
undefined method `status' for #
Propagationrecords contains a status_id as a foreign key and I have mapped propagationrecords and status using:
class Propagationrecord < ActiveRecord::Base
belongs_to :status
end
class Status < ActiveRecord::Base
has_many :propagationrecords
end
Anyone encountered this, or have I simply made a mistake in my approach?
Hi, thanks a lot.. that really some air. I was really struggling with the documentation before I read your article. I think your article should be replaced by the present documentation!
I struggled for a few days before I worked out how to get selected working so I'll post here for others:
<%= select_tag(:selCuisine, "" + options_from_collection_for_select(@cuisines, :id, :cuisinename, params[:selCuisine].to_i) ) %>
Note the ".to_i" <--that had me stumped for a while, then after thinking about it, even though the params[:selCuisine] seems like an integer, its not, hence the need to convert it to an integer for the selected option to be able to use it.
Hope this helps folks...
I just stumbled across this article... and I am also looking for something similar that "czar " had mentioned in his comments.
Let me put it this way--> in html select we have a provision where you can select a particular option using the keyword selected in the options, do we have similar thing in the rails form helper "select_tag" while the options are fetched dynamically from database tables.... Thanks in advance :)
Hey,
Good article man. One thing I've been trying to do that's driving me crazy(I'm a rails and ruby newbie) is get an onchange property on the select box to call a helper or controller method, passing to it the value of what was selected in the select box. Nothing I've tried worked.
Have you ever done that? Any tips?
I have done as you have said but it not working yet.
I have saved the value of the option selected into an instance variable (ruby) named selected_variable as.....<%= select_tag "sort_type", options_for_select([ "new", "pending", "approved", "garbage" ], "<%@selected_variable%>" ) %>....but once i submit the page...i get this error........... ERROR : syntax error, unexpected $undefined, expecting ')' . _erbout.concat " "; _erbout.concat(( select_tag "sort_type", options_for_select([ "new", "pending", "approved", "garbage" ], "<%@selected_supplier).to_s); _erbout.concat "\" ) %>\n" ................................... i am trying everything from a week but not yet successful.... thanks a lot for previous answer
thats really a great article...but i have a problem...please help me.
<%= select_tag "sort_type", options_for_select([ "new", "pending", "approved", "garbage" ], "new" ) %>
This is what i have created using the example of visa and mastercard that you have given. Now, i have a submit button bellow the above code. When i select any of option selected and submit it, the control should remain only on the option SELECTED and not the "new" all the time. So in all i wanna put some dynamic variable in place of "new" so that everytime the page is submitted it retains the value of option selected and the not the "new" which is always going to be true in above case.
thanks.
Hi! I'm skycfc, I ask the select_tag on #rubyonrails.
I posted the article to:
http://blog.pixnet.net/zusocfc/post/5574050
and
http://zusocfc.blogspot.com/2007/06/rails.html
In Traditional Chinese :)
Thank you:)
You mentioned that:
--
Use select when you require a basic drop-down selection box populated with data not sourced from a database.
--
This isn't correct since the _choices_ arg can take a value like:
City.find(:all).collect {|c| [c.name, c.id]}
This would fetch data from the db using the City model.
In most cases the simple _select_ tag would work best.
You could even do multiple select tags but using:
{:multiple => true, :size => 5} as the html_options arg.
Thanks, really cleared up a few things for me. I have been working with ruby for a few days now and i'm loving it! :)
Thanks for the write up, it certainly makes things clearer, i agree with you that there seems to be a lack of good offical documentation on this. Can you provide some more examples of using collection_select, and accessing this from the controller please.
Thanks a lot! This is a very nice writeup.