ประหยัด enum จากการเลือกใน Rails 4.1


108

ฉันใช้ enums ใน Rails 4.1 เพื่อติดตามสีของไวน์

Wine.rb

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

ในมุมมองของฉันฉันสร้างสิ่งที่เลือกเพื่อให้ผู้ใช้สามารถเลือกไวน์ที่มีสีที่ต้องการได้

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

สิ่งนี้สร้าง 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>

'1' is not a valid colorแต่เมื่อส่งแบบฟอร์มที่ผมได้รับข้อผิดพลาดในการโต้แย้งระบุ ผมทราบดีว่านี่เป็นเพราะcolorต้องเท่ากับและไม่ได้1"1"

มีวิธีบังคับให้ Rails แปลสีเป็นจำนวนเต็มแทนที่จะเป็นสตริงหรือไม่?

คำตอบ:


203

เอาล่ะเห็นได้ชัดว่าคุณไม่ควรส่งค่าจำนวนเต็มของ enum เพื่อบันทึก คุณควรส่งค่าข้อความของ enum

ฉันเปลี่ยนอินพุตเป็นดังต่อไปนี้:

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

ซึ่งสร้าง 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>

ค่าเปลี่ยนจาก "0" เป็น "สีแดง" และตอนนี้เราพร้อมแล้ว


หากคุณกำลังใช้ text_field ราง ol ปกติมันจะ:

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


หากคุณต้องการมีแอตทริบิวต์ที่มนุษย์อ่านได้ที่สะอาดคุณสามารถทำได้:

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


8
FWIW ฉันต้องทำให้ได้f.select :color, Wine.colors.to_a.map { |w| [w[0].humanize, w[0]] }เพราะwเป็นตัวแทนของอาร์เรย์ ไม่แน่ใจว่าทำไม แต่อาจจะช่วยใครบางคนได้
jakealbaugh

การใช้titleizeอาจเป็นความคิดที่ดีกว่าถ้าคุณมีคำสองคำขึ้นไป
อันวาร์

8
keysวิธีการส่งกลับอาร์เรย์เพื่อให้to_aซ้ำซ้อน อีกวิธีหนึ่งคือวิธีการเลือกผู้ช่วยจะใช้อาร์เรย์ 2 มิติสำหรับตัวเลือกเพื่อให้คุณสามารถto_aใช้ได้
xander-miller

จะเกิดอะไรขึ้นหากส่งสตริงที่ไม่ถูกต้องเช่น "ERROR_JUNK_Submission" เห็นได้ชัดว่าไม่มีค่า enum เช่นนี้และมีการโยนข้อยกเว้น เราจะจับมันได้ที่ไหน?
FlyingV

รุ่น Wine มี enum เรียกว่า color enum color: [:red, :white, :sparkling]ดังนั้นประโยคที่ถูกต้องคือf.input :color, :as => :select, :collection => Wine.color.keys.to_a
hguzman


10

ฉันเพิ่งรวบรวม EnumHelper ที่ฉันคิดว่าจะแบ่งปันเพื่อช่วยให้ผู้ที่ต้องการฉลากและสถานที่ที่กำหนดเองสำหรับ enum ของคุณเลือก

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

ในภาษาของคุณ:

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

ในมุมมองของคุณ:

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

1
การแก้ไขทำให้คีย์การแปลสามารถอ่านได้มากขึ้นโดยการค้นหา enums ที่เป็นของ MultipleWordClassName ภายใต้ multiple_word_class_name ที่ค่อนข้างเป็นปกติมากกว่า
multiwordclassname

วิธีการเพิ่มค่าเริ่มต้นสำหรับทางเลือกที่สง่างาม: [I18n.t("activerecord.enums.#{klass.underscore}.#{enum}.#{key}", default: key.humanize), key] นอกจากนี้ให้ลบคำสุดท้ายออกhumanizeเนื่องจากอาจบิดเบือนการใช้อักษรตัวพิมพ์ใหญ่หากมีหลายคำ
Peter P.

9

วิธีแก้ปัญหาที่ยอมรับไม่ได้ผลสำหรับฉันสำหรับ มนุษย์ที่อ่านได้แต่ฉันสามารถทำให้มันทำงานได้ดังนี้:

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

นี่เป็นสิ่งที่สะอาดที่สุด แต่ฉันจำเป็นต้องทำให้กุญแจของฉันมีมนุษยธรรม:

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

1
ราง 5 รองรับวิธีที่สะอาดที่สุดตอนนี้
V-SHY

@ V-SHY ทางนี้คืออะไรอธิบายได้ไหม?
ismailarilik

2
@ismailarilik ให้คีย์โดยตรงเป็นตัวเลือกที่เลือก <% = f.select (: color, Wine.colors.keys)%>
V-SHY

6

หากคุณใช้ enum ใน Rails 4 ให้โทรModel.enums:

f.select :color, Wine.colors.keys

ในการสร้าง HTML:

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

หรือเพิ่มวิธีในตัวควบคุม:

def update_or_create
    change_enum_to_i
    ....
end

def change_enum_to_i
    params[:f]["color"] = params[:f]["color"].to_i
end

4

นี่คือสิ่งที่ใช้ได้ผลสำหรับฉัน Rails 4+:

class Contract < ApplicationRecord

enum status: { active:  "active", 
               ended: "active", 
               on_hold: "on_hold", 
               terminated:  "terminated", 
               under_review:  "under_review" , 
               unknown: "unknown" 
              }


end

ในmy _form.html.erbฉันมีสิ่งนี้:

  <div class="field">
    <%= form.select :status, Contract.statuses.keys, {}%>
  </div>

ทดสอบจากคอนโซลหลังจากเพิ่มบันทึก:

2.3.0 :001 > Contract.last.status
  Contract Load (0.2ms)  SELECT  "contracts".* FROM "contracts" ORDER BY "contracts"."id" DESC LIMIT ?  [["LIMIT", 1]]
 => "active"

3

หากคุณต้องการจัดการ i18n ตามคีย์ enum คุณสามารถใช้:

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

และใน tranlations คุณสามารถตั้งค่าสี:

wine:
 red: Red
 white: White

1

นี่คือวิธีแก้ปัญหาของฉัน (บทบาทของฉันมีเครื่องหมายขีดล่างเช่น "sales_rep") และด้วยเหตุผลบางประการนี่คือวิธีที่ฉันต้องได้รับตัวเลือกว่างในการทำงาน (ด้วยรูปแบบง่าย?):

ใน ApplicationHelper:

def enum_collection_for_select(attribute, include_blank = true)
  x = attribute.map { |r| [r[0].titleize, r[0]] }
  x.insert(0,['', '']) if include_blank == true
  x
end

จากนั้นในรูปแบบของฉัน:

<%= f.input :role, collection: enum_collection_for_select(User.roles), selected: @user.role %>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.