How to have a drop down <select> field in a rails form?
Asked Answered
S

9

85

I am creating a scaffold -

rails g scaffold Contact email:string email_provider:string 

but I want the email provider to be a drop down (with gmail/yahoo/msn as options) and not a text field. How can I do this ?

Showery answered 1/1, 2013 at 18:27 Comment(0)
E
106

You can take a look at the Rails documentation . Anyways , in your form :

  <%= f.collection_select :provider_id, Provider.order(:name),:id,:name, include_blank: true %>

As you can guess , you should predefine email-providers in another model -Provider , to have where to select them from .

Endogen answered 1/1, 2013 at 18:39 Comment(3)
so the model will be contact.rb, but where should I put that drop down logic ? sorry for naive question, I am new to RoR developmentShowery
Your question is OK . If you take a look at the structure of your app/views/contacts , you'll find a file _form.html.erb . You can try to place it there . This "partial view" is in charge of both creating and updating actions of the scaffold you've generated.Endogen
recommend moving the order(:name) to a scope (which is a method) in the model. It's not a big deal here but over time you will find that having, what is essentially business logic like this (ordering) on a view template becomes a mess. Move it to the controller, or ideally the model and make a scope available there that you can use. One example - if you end up with three screens or templates that use the dropdown, having the order in the views means 3 X duplication. Having it in the model means it is defined in one place and that's the only place to change it which is good.Hindbrain
Q
63

Or for custom options

<%= f.select :desired_attribute, ['option1', 'option2']%>
Quarterstaff answered 5/7, 2015 at 2:12 Comment(1)
<%= f.select :desired_attribute, options_for_select([['opt1'], ['opt2']]) This works for meGayn
H
17

You create the collection in the Contact controller -

app/controllers/contacts_controller.erb 

Adding

@providers = Provider.all.by_name

to the new, create and edit methods, using a scope for the by_name in the Provider model - app/models/provider.rb - for the ordering by name

scope by_name  order(:name)

Then in the view - app/views/contacts/_form.html.erb - you use

<%= f.collection_select :provider_id, @providers, :id, :name, include_blank: true %>

For rails forms, I also strongly recommend you look at a form builder like simple_form - https://github.com/plataformatec/simple_form - which will do all the heavy lifting.

Hindbrain answered 1/1, 2013 at 19:42 Comment(1)
Thanks Michael, I edited the question and have put another code which worked for me, so just wondering what is the difference in using Select and collection_select as mentioned in below answers ?Showery
P
11

This is a long way round, but if you have not yet implemented then you can originally create your models this way. The method below describes altering an existing database.

1) Create a new model for the email providers:
$ rails g model provider name

2) This will create your model with a name string and timestamps. It also creates the migration which we need to add to the schema with:
$ rake db:migrate

3) Add a migration to add the providers ID into the Contact:
$ rails g migration AddProviderRefToContacts provider:references

4) Go over the migration file to check it look OK, and migrate that too:
$ rake db:migrate

5) Okay, now we have a provider_id, we no longer need the original email_provider string:
$ rails g migration RemoveEmailProviderFromContacts

6) Inside the migration file, add the change which will look something like:

class RemoveEmailProviderFromContacts < ActiveRecord::Migration
  def change
    remove_column :contacts, :email_provider
  end
end

7) Once that is done, migrate the change:
$ rake db:migrate

8) Let's take this moment to update our models:
Contact: belongs_to :provider
Provider: has_many :contacts

9) Then, we set up the drop down logic in the _form.html.erb partial in the views:

  <div class="field">
    <%= f.label :provider %><br>
    <%= f.collection_select :provider_id, Provider.all, :id, :name %>
  </div>

10) Finally, we need to add the provders themselves. One way top do that would be to use the seed file:

Provider.destroy_all

gmail = Provider.create!(name: "gmail")
yahoo = Provider.create!(name: "yahoo")
msn = Provider.create!(name: "msn")

$ rake db:seed

Panga answered 1/11, 2015 at 11:53 Comment(0)
L
7

<%= f.select :email_provider, ["gmail","yahoo","msn"]%>

Larvicide answered 15/4, 2019 at 21:42 Comment(0)
F
7

Rails drop down using has_many association for article and category:

has_many :articles

belongs_to :category

<%= form.select :category_id,Category.all.pluck(:name,:id),{prompt:'select'},{class: "form-control"}%>
Frustrated answered 24/5, 2019 at 10:32 Comment(0)
C
6

Please have a look here

Either you can use rails tag Or use plain HTML tags

Rails tag

<%= select("Contact", "email_provider", Contact::PROVIDERS, {:include_blank => true}) %>

*above line of code would become HTML code(HTML Tag), find it below *

HTML tag

<select name="Contact[email_provider]">
  <option></option>
  <option>yahoo</option>
  <option>gmail</option>
  <option>msn</option>
</select>
Cyzicus answered 1/1, 2013 at 18:33 Comment(1)
thanks, I am still confused, I understand the <select> code will go in _form.html.erb but where will that select("Contact",) code go ?Showery
E
4

In your model,

class Contact
  self.email_providers = %w[Gmail Yahoo MSN]
  validates :email_provider, :inclusion => email_providers
end

In your form,

<%= f.select :email_provider, 
    options_for_select(Contact.email_providers, @contact.email_provider) %>

the second arg of the options_for_select will have any current email_provider selected.

Example answered 16/10, 2014 at 20:33 Comment(0)
G
0

I wanted to display one thing (human readable) but store another (an integer id).

Small example

Here's a small example that helped:

<%= form.select(:attribute_name, {cat: 5, dog: 3} )%>

The {cat: 5, dog: 3} will display "cat" and "dog", but save 5 and 3.


Real world example

Here's the actual use case. It displays the names of sellers (that humans can read), but saves the sellers' id (an integer):

<div class="field">
  <%= form.label :seller_id %>
  <%= form.select :seller_id, seller_names_and_ids(), {include_blank: true}, {required: true, class: "form-control"} %>
</div>

And the helper is defined as:

def seller_names_and_ids
  # We want this to produce a hash of keys (the thing to display) and values (the thing to save, 
  # in thise case the seller_id integer)
  sellers = Seller.all
  h = {}
  sellers.each do |seller|
    thing_to_display = seller.name + " (" + seller.id.to_s + ")"
    thing_to_save_in_db = seller.id
    h.store(thing_to_display, thing_to_save_in_db) 
  end
  h
end
Gereron answered 25/11, 2022 at 0:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.