Hommer Smith
Hommer Smith

Reputation: 27852

Multiple values for collection_select

I am trying to have the following HTML code in my form:

 <select name="user[language_ids][]">
    <option value="">Please select</option>
    <option value="1" selected="selected">English</option>
    <option value="2">Spanish</option>
  </select>

  <select name="user[language_ids][]">
    <option value="">Please select</option>
    <option value="1" selected="selected">English</option>
    <option value="2">Spanish</option>
  </select>

To allow the User to select two languages when he signs up.

I have tried with this:

<%= f.label :languages %>
<%= f.collection_select(:language_ids, Language.all, :id, :name) %>
<%= f.collection_select(:language_ids, Language.all, :id, :name) %>

And also with this:

<%= f.label :languages %>
<%= f.collection_select(:language_ids[], Language.all, :id, :name) %>
<%= f.collection_select(:language_ids[], Language.all, :id, :name) %>

After revieweing the answers, I have tried with this:

<%= collection_select(:user, :language_ids, Language.all, :id, :name, {}, {:name => 'user[language_ids][]' }) %>
    <%= collection_select(:user, :language_ids, Language.all, :id, :name, {}, {:name => 'user[language_ids][]' }) %>

But the problem here is that both of the selects have the same ID and also, they are not associated with the form builder f

Any thoughts on the best way to do so?

Upvotes: 7

Views: 16808

Answers (10)

Carlos Lima
Carlos Lima

Reputation: 1

uses the gem, gem "cocoon"

in the form of the model you want to add the field

  <div class="form-group">
    <%= f.fields_for :photoproducts do |forms_fields| %>
      <% render partial: "shared/photoproducts_fields", locals: {f: forms_fields} %>
    <% end %>
    <%= link_to_add_association('+ Foto', f, :photoproducts, type: "button", class: "btn btn-primary btn-sm") %>
  </div>

(In this example, I have a product template, which I'm adding photos to)

and then create a partial in a shared folder at the root of the project.

-

project / shared / _fotoproduto_fields.html.erb

<div class="nested-fields">
  <%= link_to_remove_association('remove photo', f, type: "button", class: "btn btn-danger btn-sm") %>
  <div class="photoproducts">
    <div class="form-group">
      <label class="control-label"><%= f.label :name %></label>
      <%= f.text_field :name, class: "form-control", type: "text" %>
    </div>
    <div class="form-group">
      <label class="control-label"> photo</label>
      <%= f.file_field :files, class: "form-control" %><br/><br/>
    </div>
  </div>
</div>

this way you get a more dynamic form

remember to put parameters in the controllers for nested attributes, and put in the create method, a build of the model.

ex: @products.photoproducts.build

Upvotes: 0

G SubbaRao
G SubbaRao

Reputation: 446

Try This:

<%=select_tag "language_ids[]" options_for_select([['Please select','']]+Language.all.map{|ln|[ln.name,ln.id]})%>

Upvotes: 0

user1003545
user1003545

Reputation: 2118

You can create a collection of fields by using fields_for.
The two select will have the same id but collection_select takes an optional hash containings html options where you can set a custom id.

<%= f.fields_for :language_ids do |language| %>
  <%= language.collection_select(nil, Language.all, :id, :name,
        {include_blank: "Please select", selected: 1},
        {id: :user_language_id_1}) %>
  <%= language.collection_select(nil, Language.all, :id, :name,
        {include_blank: "Please select", selected: 1},
        {id: :user_language_id_2}) %>
<% end %>

generates the following output:

<select id="user_language_id_1" name="user[language_ids][]">
  <option value="">Please select</option>
  <option value="1" selected="selected">English</option>
  <option value="2">Spanish</option>
</select>

<select id="user_language_id_2" name="user[language_ids][]">
  <option value="">Please select</option>
  <option value="1" selected="selected">English</option>
  <option value="2">Spanish</option>
</select>

Upvotes: 3

ChuckE
ChuckE

Reputation: 5688

Since you wrote "user[language_ids][]" in the name attribute of your select, I'm forced to assume you want a multiple select box. You should do this:

f.collection_select (:language_ids, Language.all, :id, :name, 
{:prompt => true}, {:multiple => true})

Not a very well documented feature, for sure.

EDIT: Looking further on your example and some of the answers, I would like to point out a few things:

  • Your example has the prompt "Please select" as first option of the select box, but then you want english to be "selected in both of your examples. If you want an option to be pre-selected which it as well the first option of the select box, then why do you have the prompt option? One uses it to force the user to choose something.

  • You wrote in one answer that you want multiple selects instead of a multiple select, reason why you don't want the :multiple => true solution. I'd still recommend you to use it, but maybe you really have a necessary use case. Therefore, I would recommend you to use the select_tag helper directly (I don't think you'll achieve that with form tag helpers):

    select_tag "user[language_ids][]", 
    options_from_collection_for_select(Language.all, "id", "name"), 
    :prompt => true, :id => "user_language_ids"
    

    key here is, the mutiple selects have to have different ids, which you will have to pass explicitly, so it'll be on your hands.

Upvotes: 2

nikolayp
nikolayp

Reputation: 17919

First of all you have to edit your user model:

user.rb

has_many :languages
accepts_nested_attributes_for :languages

Next generate model:

$> rails g model language user_id:integer name

language.rb

belongs_to  :user

users_controller.rb (probably in 'new' action)

2.times do
  language = @user.language.build
end

_form.html.erb

<%= form_for @user do |f| %>
  <%= f.fields_for :languages do |lang| %>
    <%= lang.label :name, "Name" %><br />
    <%= lang.select :name, Language.all.collect {|l| [l.name, l.id]}, prompt: 'Please select' %><br /> 
  <%= end %>     
  <p><%= f.submit "Submit" %></p>
<% end %>

Upvotes: 0

Emilio Blanco
Emilio Blanco

Reputation: 11

You could use fields_for like this:

<%= form_for @user do |f| %>
 ...
<h2>Languages</h2>
<%= fields_for :languages do |lang_form| %>
  <%= lang_form.collection_select :language_id, Language.all, :id, :name %>
<% end %>
..
<% end %>

On your controller, in your new or edit method, you can do this to accept 2 languages

2.times { @user.languages.build }

Upvotes: 0

webmagnets
webmagnets

Reputation: 2296

You could add a "second_language" column to your table and then make it have the same content as the first language column.

Upvotes: 0

charlysisto
charlysisto

Reputation: 3700

Basically sending 2 (or more) fields with the same attribute doesn't work, because params will be turned into a hash : {language_ids: [1], language_ids: [2]} and since you have the same key the last one will override the previous one(s).

{a: 1, a: 2}[:a] #=> 2

To circumvent this problem you need to to create different attributes and then concatenate them in one. For this you'll need facade attributes in your model :

class Language
  attr_writer :language_one_id, :language_two_id

  before_save :gather_languages

  def gather_languages
    self.language_ids= [language_one_id, language_two_id]
  end

  # for the form to display chosen lang (idem with two)
  def language_id
    language_ids.first
  end
end

# view/_form
=f.select :language_one_id, Language.all.map {|l| [l.name, l.id]}
=f.select :language_two_id, Language.all.map {|l| [l.name, l.id]}

But frankly I think you're making your life harder by going against conventions here (I'd use checkboxes instead).

Upvotes: 0

James Mason
James Mason

Reputation: 4296

You should be able to do something like this:

<%= f.label :languages %>
<%= f.collection_select(:language_ids, Language.all, :id, :name, {}, {id => "language_1", :name => "user[language_ids][]"}) %>
<%= f.collection_select(:language_ids, Language.all, :id, :name, {}, {id => "language_2", :name => "user[language_ids][]"}) %>

Upvotes: 1

shweta
shweta

Reputation: 8169

Try,

<%= f.collection_select(:language_ids, Language.all, :id, :name,{}, {:multiple => true}) %>

Upvotes: 8

Related Questions