我目前有一个带有深度嵌套的复杂表单,我正在使用Cocoon gem来根据需要动态添加部分(例如,如果用户想要在销售表单中添加其他车辆)。代码如下所示:
<%= sale.fields_for :sale_vehicles do |sale_vehicles_builder| %>
<%= render :partial => "sale_vehicles/form", :locals => {:f => sale_vehicles_builder, :form_actions_visible => false} %>
<% end -%>
<div class="add-field-links">
<%= link_to_add_association '<i></i> Add Vehicle'.html_safe, sale, :sale_vehicles, :partial => 'sale_vehicles/form', :render_options => {:locals => {:form_actions_visible => 'false', :show_features => true, :fieldset_label => 'Vehicle Details'}}, :class => 'btn' %>
</div>
这对于第一级嵌套非常有效 - sale_vehicle
对象由Cocoon正确构建,并且表单按预期呈现。
当存在另一级别的嵌套时会出现问题 - sale_vehicle
部分看起来像这样:
<%= f.fields_for :vehicle do |vehicle_builder| %>
<%= render :partial => "vehicles/form", :locals => {:f => vehicle_builder, :f_parent => f, :form_actions_visible => false, :show_features => true, :fieldset_label => 'Vehicle Details'} %>
<% end -%>
vehicle
的部分呈现时没有字段,因为没有构建sale_vehicle.vehicle
个对象。
我需要做的是构建嵌套对象和主对象(Cocoon目前不构建任何嵌套对象),但最好如何做到这一点?有没有办法从帮助程序代码中选择嵌套表单,以便可以构建它们?
Cocoon目前正在构建这样的主要对象:
if instance.collection?
f.object.send(association).build
else
f.object.send("build_#{association}")
end
如果我可以执行以下操作,它会使事情变得简单明了,但我不确定如何获取f.children
- 有没有办法从父窗体构建器访问嵌套窗体构建器?
f.children.each do |child|
child.object.build
end
任何有助于实现此功能的帮助,或建议另一种动态构建这些对象的方法。
谢谢!
编辑:可能值得一提的是,这个问题似乎与上面提到的Cocoon宝石以及Ryan Bates的nested_form宝石有关。 Cocoon gem的Issue #91似乎与此问题相同,但dnagir建议的解决方法(委托构建对象)在这种情况下并不理想,因为这会导致其他形式的问题。 / p>
答案 0 :(得分:26)
我可以在你的第二个嵌套表单中看到没有link_to_add_association
。
在cocoon内部,link_to_add_association
会构建一个新元素,以供用户想要动态添加它。
或者,您是否暗示一旦构建sale_vehicle
,它应自动包含vehicle
?我会假设用户必须选择出售的车辆?
我有一个test-project来演示双嵌套表单:项目有任务,可以有子任务。
但也许这与你想做的事情没有足够的关系?
您不会展示您的模型,但如果我理解正确的关系
sale
has_many :sale_vehicles
sale_vehicle
has_one :vehicle (has_many?)
因此,如果您的sale_vehicle
可以有vehicle
,那么我认为您的用户会首先将sale_vehicle
添加到sale
,然后点击链接以添加vehicle
。这就是茧能做得非常好的事情。另一方面,如果您希望当cocoon动态创建sale_vehicle
时,还会创建vehicle
,我会看到一些不同的选项。
after_initialize
不能说我真的很喜欢这个,但在你after_initialize
的{{1}}回调中,你总是可以构建所需的sale_vehicle
模型。
我在此假设,由于您的vehicle
无效/在没有sale_Vehicle
模型的情况下无法存在,因此模型的责任是在构建时立即创建嵌套模型。
请注意,每个对象创建都会执行vehicle
,因此这可能会很昂贵。但这可能是一个快速解决方案。如果你拒绝空的嵌套模型,这应该可以工作。
对于用户来说,after_initialize
和sale_vehicle
似乎是一个对象,那么为什么不创建一个由sale_vehicle和一个车辆组成的装饰器,它被呈现为一个(嵌套)形式并在保存时这个,装饰者知道它需要保存到正确的模型中。
注意:这有不同的用语。装饰器通常只使用一些视图方法扩展单个类,但它也可以是不同模型的组合。替代术语:演示者,视图模型。
无论如何,装饰器/演示者的功能是为用户抽象出基础数据模型。因此,无论出于何种原因,您需要将单个实体拆分为两个数据库模型(例如,限制列的nr,以保持模型可读,......)但对于用户而言,它仍然是单个实体。所以“呈现”它作为一个。
vehicle
方法不确定我是否喜欢这个,但这绝对是可能的。如果“嵌套模型”不是ActiveRecord :: Association,则已经支持它,所以这不应该太难添加。但我对这一补充犹豫不决。所有这些选项都使它变得更加复杂。
在你的局部内部,只需构建所需的子对象。这必须在build
之前发生,然后你就好了。像
fields_for
我个人非常喜欢装饰器方法,但它可能有点沉重。只需在渲染<% f.object.build_vehicle %>
<%= f.fields_for :vehicle do |vehicle_builder| %>
<%= render :partial => "vehicles/form", :locals => {:f => vehicle_builder, :f_parent => f, :form_actions_visible => false, :show_features => true, :fieldset_label => 'Vehicle Details'} %>
<% end -%>
之前构建对象,这样就可以确保至少有一个。
我很想听听你的想法。
希望这会有所帮助。