0

见我的问题:Rails的accepts_nested_attributes_for + HABM返回数组与空字符串

class MedicalRecord < ActiveRecord::Base 
    has_many :evaluations, dependent: :destroy 

    accepts_nested_attributes_for :evaluations, allow_destroy: true, reject_if: :all_blank 
end 

class Evaluation < ActiveRecord::Base 
    belongs_to :medical_record 

    has_and_belongs_to_many :edemas 

    validates :description, presence: true 
end 

我的形式展示具有多个属性的选择字段。

<%= form_for @medical_record do |f| %> 
    <%= f.fields_for :evaluations do |e| %> 
    <%= e.text_field :description %> 

    <%= e.collection_select :edema_ids, Edema.all.order(:title), :id, :title, 
     { }, multiple: true %> 
    <% end %>  
<% end %> 

当我没有选择任何'水肿',表单发送一个数组,其中包含一个空字符串。所以,reject_if返回false,我需要填写说明字段。在这种情况下,reject_if应返回true

我该怎么办才能使其工作?

非常感谢

+0

您可以包含为当前表单渲染的*当前*(处于“破碎”状态)的HTML吗? – jasonmklug

回答

1

如果您的目标仅仅是在您的:edema_ids阵列中没有空字符串,则可以将include_hidden: false传递给collection_select

也就是说,如果您最终希望“取消”现有评估的所有edemas,将会导致问题,因为如果浏览器的值真的是空的,它将根本不会发送:edema_ids数组(因为它是多选) ,基本上同样的原因,它不会发送未勾选的复选框值。在空数组中包含空字符串是帮助程序处理此浏览器行为的方式。

相信它的最好让HTML表单提交空字符串数组元素(因此,浏览器和Rails可以每个像预期的那样在这种情况下),以及使用ActiveRecord的reject_if: :all_blank:evaluations协会(假设确实是你正在寻找的行为)。

您可以通过在:edema_ids传入控制器之后但在通过:all_blank评估它之前从空白字符串元素中剥离出空字符串元素,而无需进入带有空白字符串的挂断来实现此目的。

一种近乎:

# Inside MedicalRecordsController 

def create 
    @medical_record = MedicalRecord.create(medical_record_params) 
end 

def update 
    @medical_record = MedicalRecord.find(params[:id]) 
    @medical_record.update_attributes(medical_record_params) 
end 

private 

    def medical_record_params 
    # I assume you're using strong params to control what can be passed 
    # through the controller. If so, manipulate the params *after*  
    # calling .require() and .permit() on the params hash 
    remove_empty_string_from_edema_ids(params) 
    end 

    def remove_empty_string_from_edema_ids(params_hash) # Use a better name than this 
    params_hash[:evaluations].each do |evaluation| 
     # Don't forget to use guard clause to prevent calling a 
     # method on nil if :edema_ids is not present in evaluation 
     evaluation[:edema_ids].reject!(&:empty) if evaluation[:edema_ids] 
    end 
    end 

本(或类似的东西,因为有很多方法可以实现同样的结果)会 - 除了正确造成:all_blank返回true如果:evaluation确实是所有空白 - 导致将真正空的数组传递给评估的edema_ids=()方法;这将由于某些ActiveRecord关联魔术而导致从Evaluation实例中删除所有:edemas

+0

这些是广泛的笔画。但是,再次查看控制器代码,我错误地使用了'medical_record_params'。会稍微更新一下... – jasonmklug

0

你可能会想编写自定义reject_if方法。如果您提供了一个符号,ActiveRecord将使用该名称在当前类中查找一个方法,并将提交的属性散列传递给evaluation

class MedicalRecord < ActiveRecord::Base 
    accepts_nested_attributes_for :evaluations, allow_destroy: true, reject_if: :essentially_blank 

    def essentially_blank(attributes) 
    attributes[:description].blank? && attributes[:edema_ids][0].blank? 
    end 
end 

注意的是,虽然[""].blank?是假"".blank?是真实的,这就是为什么我得到的数组的第一个对象,并检查其blank -ness。

您可以查看the Rails API以查看有关如何在reject_if上完成自定义行为的更多示例。

+0

我认为可以存在一种方法来发送一个空数组而不是一个空字符串的数组。好的,写一个自定义的reject_if,它应该可以工作。我正在避免这种方式,因为有5 + HABM评估:(感谢Lanny –

+0

我倾向于修复空字符串的存在(可以传递'include_hidden:false'到您的'collection_select'),而不是写一个自定义检查空白的方法 但是,如果你使用自定义方法路线,确保它足够灵活以适应最终在“评估”中增加的新属性,如果在路上想添加一个':title'属性到'评估',并通过评估发送一个有效的':title'字符串,但空白的':description'和一个带有空字符串的':edema_ids'数组,这个'essentially_blank()'方法仍然会评估为'true' – jasonmklug

+0

@jasonmklug,我不知道'include_hidden:false',你只需要阅读它,这也是有道理的。你如何避免相反的情况(用户明确地想要从协会中删除所有的孩子)? –