use collection_select
<%= collection_select("job", "client_id" , @clients, "id", "name") %>
job is the object (model row) and client_id is the column of job to set. @clients is a collection of Client objects perhaps read from a table of clients, id is the client column whose value will be used to set the client_id and name is the client column that contains the text to be displayed.
(You can use @clients = Client.find_all to get the clients collection or you can put the “Client.find_all” directly into the collection_select instead of ”@clients”. Like this <= collection_select :Job, :client_id, Client.find(:all), :id, :name >).
This would create:
<select name="job[client_id]">
<option value="1">Francisco Hernandez</option>
<option value="2">James Earl</option>
</select>
select() is meant to operate on ActiveRecord object instances, or, more generally, any class instance which has read accessor methods, à la attr_reader.
ex1:
select (:artists, :name, @deadArtists)
will create a drop-down select list for all of the artists in @deadArtists. Each deadArtist is checked to see if they match the value of @artists.name. If they match, they will be selected in the created drop-down list.
You say you want to use some other data structure – a hash or an array – and you are determined to use the select() helper function? You can either force your data into a data structure that can be accessed via the dot-accessor (e.g., by using the Struct object), or you can use the :selected option.
ex2:
#controller:
@SiteLanguages = ['en', 'de', 'fr']
...
pLanguages = params[:languages]
@languages = Struct.new(:language).new(pLanguages['language'])
#view:
<%= select :languages, :language, @SiteLanguages %>
The compact single-line syntax above could also be written as:
MyStruct = Struct.new(:language)
@languages = new MyStruct(pLanguages['language'])
ex3:
Using the :selected option
@SiteLanguages = ['en', 'de', 'fr']
@selected_language = 'en'
#view:
<%= select :languages, :language, @SiteLanguages, {:selected=>@selected_language} %>
You CAN use “options_from_collection_for_select” where the last argument is an integer to match the id of the selected item.
To force a selected value, use the :selected value
select(:lead, :months, month_map, {:selected => 6}, {}) # 6 will always be pre-selected
select(:lead, :months, month_map, {}, {}) # @lead.months [or nothing, if that's nil] will be pre-selected
I used the select helper to give the user a choice to select the sex of a person.
The helper code is as follows:
<%= select( "member", "sex", { "male" => "M", "female" => "F"}) %>
This would render this HTML:
<select id="member_sex" name="member[sex]">
<option value="F">female</option>
<option value="M" selected="selected">male</option>
</select>
Rails would assign the values M or F for this attribute in the database, and pre-select the right value if the view is rendered for editing.
(Why does the hash get sorted, F before M? This isn’t significant here but it’s a problem with what I’m using (a huge list that needs to remain in a fixed order)
You can never be guaranteed of any order with hashes. You could try .sort() to sort the values in alphabetical order.
When trying to get multiple selected values from a <SELECT> tag into @params, call your parameter something with [] on the end. That way rails knows it’s multiple.
So instead of
<select name="job[techs]" size="5" multiple="multiple">
Use:
<select name="job[techs][]" size="5" multiple="multiple">
<% @selected = @job.technician.collect { |t| t.technician_id.to_i } %>
<select name="job[techs][]" size="5" multiple="multiple">
<%= options_from_collection_for_select(@techs, "id", "full_name", @selected) %>
</select>
If you want to have your multiple choice select assign to a has_many or has_and_belongs_to_many (habtm) collection on your object, you should specify the collection using the <collection name>_ids name. For example, if we have an AddressGroup object with a collection of Address objects called addresses, we would not use this:
<select name="addressgroup[addresses][]" multiple="multiple">
but rather this:
<select name="addressgroup[address_ids][]" multiple="multiple">
With either select statement the returned value is an array of Address object ids, not the actual array objects, which will fail if we tried to assign to addressgroup.addresses. If you try to do that you will get an error message like “Expected Address and got String”. Instead, we use the implicit address_ids= property (automatically added to an object when it has a has_many or habtm collection), which assigns to the collection based on a collection of IDs.
Sometimes you may want to use multiple attributes for the text portions of the select list. or maybe you want to munge a value in some specific way.
<select id="device_outlet" name="device[outlet]">
<option value=""></option>
<%= options_from_collection_for_select @outlets.collect {|outlet| [outlet.id, outlet.powerstrip.name + ":" + outlet.name]}, 'first', 'last', @selected_outlet %>
</select>
By creating a collection of arrays with 2 values (the first being the option value and the second being the option text) you can munge/change/concatenate to your hearts content. The trick is use the first and last methods for an Array.
If you want you can always sort by the option text as well:
<select id="device_outlet" name="device[outlet]">
<option value=""></option>
<%= options_from_collection_for_select @outlets.collect {|outlet| [outlet.id, outlet.powerstrip.name + ":" + outlet.name]}.sort{|x,y| x.last <=> y.last}, 'first', 'last', @selected_outlet %></select>
—Joel Nimety
Or just add a method to the model that concatenates the fields and use that in the call to collection_for_select.
To answer the question “How would one go about adding html options to collection_select_with_current?” Try this:
def collection_select_with_current(object, method, collection, value_method, text_method, current_value, html_options={})
result = "<select name='#{object}[#{method}]'"
html_options.each do |key, value|
result << ' ' + key.to_s + '="' + value.to_s + '"'
end
result << ">\n"
for element in collection
if current_value == element.send(value_method)
result << "<option value='#{element.send(value_method)}' selected='selected'>#{element.send(text_method)}</option>\n"
else
result << "<option value='#{element.send(value_method)}'>#{element.send(text_method)}</option>\n"
end
end
result << "</select>\n"
return result
end
Use html_options like so:
collection_select_with_current('entry', 'author_id', @authors, 'id', 'full_name', @entry.author_id, {:class => 'my_select'})
Or leave out the html_options like this:
collection_select_with_current('entry', 'author_id', @authors, 'id', 'full_name', @entry.author_id)
—Chris Your
Here is my, and hopefully the best, solution:
user.rb:
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
end
role.rb:
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
end
user_controller.rb:
class UserController < ApplicationController
def new
@user = User.new
@roles = Role.find(:all)
end
...
def edit
@user = User.find(params[:id])
@roles = Role.find(:all)
end
end
And in *.rhtml, i use:
<%= collection_select('user', 'role_ids', @roles, :id, :name, {}, :multiple => true) %>
—Christian Mueller
How to pass in disabled:
It appears that select() takes these options:
def select(object, method, choices, options = {}, html_options = {})
So if you want it disabled, you’ll need to pass that in last, or select(:abc, :option_name, ‘yes’, true?, {}, {:disabled => true }) to have a disabled version.