Saving enum from select in Rails 4.1

61,528

Solution 1

Alright, so apparently, you shouldn't send the integer value of the enum to be saved. You should send the text value of the enum.

I changed the input to be the following:

f.input :color, :as => :select, :collection => Wine.colors.keys.to_a

Which generated the following HTML:

<select id="wine_color" name="wine[color]">
  <option value=""></option>
  <option value="red">red</option>
  <option value="white">white</option>
  <option value="sparkling">sparkling</option>
</select>

Values went from "0" to "red" and now we're all set.


If you're using a regular ol' rails text_field it's:

f.select :color, Wine.colors.keys.to_a

If you want to have clean human-readable attributes you can also do:

f.select :color, Wine.colors.keys.map { |w| [w.humanize, w] }

Solution 2

No need converting the enum hash to array with to_a. This suffice:

f.select :color, Wine.colors.map { |key, value| [key.humanize, key] }

Solution 3

The accepted solution didn't work for me for the human readable, but I was able to get it to work like this:

<%= f.select(:color, Wine.colors.keys.map {|key| [key.humanize, key]}) %>

This was the cleanest, but I really needed to humanize my keys:

<%= f.select(:color, Wine.colors.keys) %>

Solution 4

I just put together an EnumHelper that I thought I'd share to help people who need more customised enum labels and locales for your enum selects.

module EnumHelper

  def options_for_enum(object, enum)
    options = enums_to_translated_options_array(object.class.name, enum.to_s)
    options_for_select(options, object.send(enum))
  end

  def enums_to_translated_options_array(klass, enum)
    klass.classify.safe_constantize.send(enum.pluralize).map {
        |key, value| [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}"), key]
    }
  end

end

In your locale:

 en:
   activerecord:
     enums:
      wine:
        color:
          red:   "Red Wine"
          white:  "White Wine"

In your views:

 <%= f.select(:color, options_for_enum(@wine, :color)) %>

Solution 5

If you use enum in Rails 4 then just call Model.enums:

f.select :color, Wine.colors.keys

To create HTML:

<select name="f[color]" id="f_color">
    <option value="red">red</option>
    <option value="white">white</option>
    <option value="sparkling"> sparkling </option>
</select>

Or add method in controller:

def update_or_create
    change_enum_to_i
    ....
end

def change_enum_to_i
    params[:f]["color"] = params[:f]["color"].to_i
end
Share:
61,528

Related videos on Youtube

Brian Weinreich
Author by

Brian Weinreich

Aloha. Brian Weinreich

Updated on June 25, 2021

Comments

  • Brian Weinreich
    Brian Weinreich almost 3 years

    I am using the enums in Rails 4.1 to keep track of colors of wine.

    Wine.rb

    class Wine < ActiveRecord::Base
        enum color: [:red, :white, :sparkling]
    end
    

    In my view, I generate a select so the user can select a wine with a certain color

    f.input :color, :as => :select, :collection => Wine.colors
    

    This generates the following HTML:

    <select id="wine_color" name="wine[color]">
      <option value=""></option>
      <option value="0">red</option>
      <option value="1">white</option>
      <option value="2">sparkling</option>
    </select>
    

    However, upon submitting the form, I receive an argument error stating '1' is not a valid color. I realize this is because color must equal 1 and not "1".

    Is there a way to force Rails to interpret the color as an integer rather than a string?

    • TPR
      TPR about 2 years
      Where did the "input" method come from? Thanks
  • jakealbaugh
    jakealbaugh almost 9 years
    FWIW, I needed to make it f.select :color, Wine.colors.to_a.map { |w| [w[0].humanize, w[0]] } because w represented an array. Not sure why, but maybe this will help someone.
  • Anwar
    Anwar over 8 years
    using titleize might be a better idea if you have two or more words
  • xander-miller
    xander-miller over 8 years
    the keys method returns an array so the to_a is redundant. Alternatively the select helper method takes a 2D array for options so you can just use to_a.
  • FlyingV
    FlyingV over 8 years
    What if an Invalid String is sent such as "ERROR_JUNK_Submission". Obviously there is no enum value such as this, and an exception is thrown. Where would we catch it?
  • V-SHY
    V-SHY over 7 years
    rails 5 supports the cleanest way now
  • ismailarilik
    ismailarilik almost 7 years
    @V-SHY, what is this way, can you explain?
  • V-SHY
    V-SHY almost 7 years
    @ismailarilik, directly provide the keys as select option, <%= f.select(:color, Wine.colors.keys) %>
  • Tetsujin no Oni
    Tetsujin no Oni over 5 years
    The edit makes the translation keys more readable by finding enums belonging to MultipleWordClassName under the rather more typical multiple_word_class_name rather than multiplewordclassname
  • Peter P.
    Peter P. over 5 years
    How about adding in a default for a graceful fallback: [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{ke‌​y}", default: key.humanize), key] Also, remove the last humanize as it may distort capitalization if its multiple words
  • hguzman
    hguzman about 4 years
    The model Wine has a enum called color enum color: [:red, :white, :sparkling]so the correct sentence is f.input :color, :as => :select, :collection => Wine.color.keys.to_a