user3183015
user3183015

Reputation: 179

Using Rails Active Record enum types with Simple_Form

I declare a model with an enum type like:

class Event < ActiveRecord::Base
    enum event_type: { "special_event" => 0,
                       "pto"           => 1,
                       "hospitality"   => 2,
                       "classroom"     => 3
                       }

Then in my update view, I have a form with:

<%= simple_form_for @event do |f| %>   
    <%= f.input :event_type, collection: Event.event_types.keys %>  
    ... 
<% end %>

This works great, and I get a select populated with my enumerated types. When I perform the @event.update(event_params) in my controller, I can check the db and see that the event_type field has been updated to the correct integer value.

However, when I revisit the edit page, the select shows a nil value. If I check its value by adding a debug line to my form:

<%= f.input :event_type, collection: Event.event_types.keys %>  
<%= debug @event %>

I see that the value for event_type is correct:

--- !ruby/object:Event
attributes:
  ...
  event_type: '2'

but the input selector is still blank rather than showing "hospitality" as it should.

Any ideas would be greatly appreciated. :)

Upvotes: 18

Views: 16782

Answers (9)

Yohannes Aregay
Yohannes Aregay

Reputation: 1

<%= f.input :event_type, collection: Event.event_types.transform_keys(&:titleize) %>

Just passing the enum hash as it is would be enough. but if you want to alter the options you can use transform_keys

Upvotes: 0

epicrato
epicrato

Reputation: 8418

For example, assuming you have an enum like this one in your model (Model Role in the example):

enum :work_type, in_person: "in_person", remote: "remote", hybrid: "hybrid"

This will work in your view

= f.input :work_type, as: :select, collection: Role.work_types.collect { |key, value| [key.to_s.titleize, value] }

This will out put the following HTML:

<select class="form__input" name="role[work_type]" id="role_work_type">
  <option selected="selected" value="in_person">In Person</option>
  <option value="remote">Remote</option>
  <option value="hybrid">Hybrid</option>
</select>

Upvotes: 2

Ben Pohl
Ben Pohl

Reputation: 148

I got stuck on this one too. I needed to titlieze my enums so they wouldn't look so wonky with the snake_case that I was using. I used to_a to take the ruby hash and turn it into an array and then I used collect to return a new array in the format that I needed.

collection: Event.event_type.to_a.collect{|c| [c[0].titleize, c[0]]}

Hopefully this will help someone else out.

Upvotes: 8

Daniel Garcia
Daniel Garcia

Reputation: 1402

Vincent's solution gives me the error: '0' is not a valid 'fieldname'

I had to add keys as suggested in other stackoverflow post: <%= f.input :event_type, collection: Event.event_types.keys %>

Upvotes: 13

Vincent
Vincent

Reputation: 443

this line worked just fine. <%= f.input :event_type, collection: Event.event_types %>

Do you have to manually set the selected value ? what's your version of simple_form ?

Upvotes: 27

Xiaohui Zhang
Xiaohui Zhang

Reputation: 1125

I'm using draper as decorator, so I'd like to add words translation into model's decorator. Here is my code:

app/decorators/meter_decorator.rb

class MeterDecorator < Draper::Decorator
  delegate_all

  STATUS_MAPPING = {
    uninitialized: '未安装',
    good: '良好',
    broken: '故障',
    disabled: '禁止'
  }

  def status
    STATUS_MAPPING[object.status.to_sym]
  end
end

views/meters/_form.html.erb

<%= f.input :status, collection: MeterDecorator::STATUS_MAPPING, label_method: :last, value_method: :first, include_blank: false %>

views/meters/index.html.erb

<td><%= meter.decorate.status %></td>

Upvotes: 0

Stan Wiechers
Stan Wiechers

Reputation: 2092

Use the enum_help gem. Lets you do this:

<%= f.input :event_type %>

Upvotes: 8

user3183015
user3183015

Reputation: 179

Thanks for following this up. I believe the issue I reported was caused by an incorrect declaration for event_type. In my migration, I had accidentally defined event_type as String rather than an Integer:

class CreateEvents < ActiveRecord::Migration
  def change
    create_table :events do |t|
      ...
      t.string :event_type
      ...
    end
  end
end

I believe that enum's should be declared as integers to work correctly. Unfortunately, I did not test that changing the type to integer makes it work because I actually ended up using a different approach. Rather than using an enum I instead defined my collection of event types in my model:

class Event < ActiveRecord::Base
  def self.types
    ['Special_Events', "On-Going", 'PTO', "Classroom"]
  end
  ...
end

And, then in my form, used simple form with this syntax:

<%= f.input :event_type, collection: Event.types, input_html: { autocomplete: 'off' }  %>

And all worked well.

Upvotes: 0

user3183015
user3183015

Reputation: 179

With some more research, I came up with the following solution:

<%= f.input :event_type, collection: Event.event_types.keys,
            :selected => Event.event_types.keys[@event[:event_type].to_i], 
            input_html: { autocomplete: 'off' }  %>

So, I had to do 2 things:

  • Use :selected to set the value of the selector. This required the cumbersome syntax Event.event_types.keys[@event[:event_type].to_i] to set the select value. I'd love to hear if there is a simpler syntax I could have used. :)
  • Set autocomplete: 'off' to prevent firefox from wanting to set the selector to its previous setting on a page reload.

Alternate, simpler solutions would be welcomed!

Upvotes: -1

Related Questions