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

Srinivasan July 14, 2008 16:06

Thanks a lot! This is a very nice writeup.

Tony July 10, 2008 15:06

Thank you, this did the trick.

Bob Walsh July 07, 2008 20:01

Finally! some decent documentation of select - thanks very much; perhaps you can contribute this back to rails doc project.

Dodger June 23, 2008 22:58

"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...

Mike June 15, 2008 18:34

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?

pepe June 08, 2008 22:21

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.

shiningthrough May 29, 2008 13:53

@Antony - Looking at your code briefly, it appears that your 'choices' parameter is neither an array, or a hash.

@Maige - I am currently working on one, watch this space!

@aampal - Both 'Select' and 'collection_select' will allow you to set a dynamic default option in the selection box.

aampal May 26, 2008 12:53

Hi,

Thanks,

But how can I select value by default or onn some event? ... or what would be the selectedIndex

maige April 24, 2008 04:23

>> 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. ;)

antony evans April 17, 2008 23:08

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 %>

Marc March 03, 2008 22:20

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}) %>

shiningthrough March 03, 2008 21:08

@Mac - There is a select example with a "GET" form on one of my other posts, you can find it here: Get and Post

@Prabhu - 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.

Prabhu March 03, 2008 07:36

Can anyone explain me how to make an AJAX call in rails on selection of a list item? Thanks in advance

Mac March 01, 2008 22:41

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

Don Kowalski February 25, 2008 02:59

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.

Robert Rees February 19, 2008 16:31

Thanks for this post. It's much clearer than the real documentation because you've contrasted the different methods and their different uses.

Eric Colon February 19, 2008 01:15

Thanks a lot! This article was extremely helpful and saved me hours of frustration.

Imran Lakhani January 10, 2008 23:11

Its was really helpful
Thanks buddy :)

shiningthrough December 15, 2007 11:41

Hi Mark, are you still getting that error? i don't think its anything to do with your select helper, it looks to me like your not creating your @statuses collection correctly. Try iterating over your @statuses collection in your view first, with something like this:


@statuses.each do |status|
@status.code
end

Get this working before displaying them in a selection box.

mark December 11, 2007 23:00

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 #"Grafted", "id"=>"4"}>

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?

Pablo December 07, 2007 15:30

Thank you, this was helpful indeen.

Pavankumar Kulkarni December 03, 2007 09:42

*cleared

Pavankumar Kulkarni December 03, 2007 09:40

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!

wildpalms November 11, 2007 05:48

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...

Dave H November 01, 2007 16:19

Thanks for the tips!

shiningthrough October 08, 2007 17:14

hi sumanth, why not just use collection_select?

sumanth October 03, 2007 17:03

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 :)

shiningthrough July 24, 2007 10:36

bleah12345, this sort of think is covered very well in Railscasts, take a look at this: http://railscasts.com/episodes/43.

bleah12345 July 20, 2007 20:15

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?

Czar July 20, 2007 18:05

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

shiningthrough July 19, 2007 09:34

Czar, if i understand you correctly you can achieve what you want by using my first example, the select helper. When the first selection has been made, store the value in either the database, a global variable, or class variable. Then @selected_payment can be set to any of these. Hope that answers your question.

Czar July 17, 2007 23:14

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.

srijith July 13, 2007 10:20

very nice 2 understand...

weeznu July 10, 2007 10:24

cool...

very nice! :)

SkyCFC June 28, 2007 20:09

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:)

shiningthrough May 31, 2007 09:13

Thanks for the feedback Nikhil, youll notice that i do give an example similar to the one you suggest, i just suggest that collection_select is preferable. I am curious to know why you think select_tag would work best in most cases?

Nikhil Gupte May 30, 2007 17:51

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.

Gaurav May 28, 2007 13:42

Nice article

Ash McConnell May 24, 2007 21:39

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! :)

shiningthrough May 15, 2007 18:36

Thanks for the kind words RMax, i will provide some more examples of using collection_select in the near future.

RMax May 14, 2007 10:03

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.

Ccea745308a2d9bcc9958e0257ecb8a904c04d15

type the text from the image