Rails-动态构建深度嵌套的对象(Cocoon / nested_form) [英] Rails - Dynamically build deeply nested objects (Cocoon / nested_form)

查看:80
本文介绍了Rails-动态构建深度嵌套的对象(Cocoon / nested_form)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前有一个具有深层嵌套的复杂表格,并且我正在使用 Cocoon gem来根据需要动态添加部分(例如如果用户想在销售表格中添加其他车辆)。代码如下所示:

I currently have a complex form with deep nesting, and I am using the Cocoon gem to dynamically add sections as required (e.g. if a user wants to add another vehicle to the sale form). Code looks like this:

<%= 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正确构建,并且表单按预期方式呈现。

This works very nicely for the first level of nesting - the sale_vehicle object is built correctly by Cocoon, and the form renders as expected.

问题是当存在另一层嵌套时- sale_vehicle 部分看起来像这样:

The problem comes when there is another level of nesting - the sale_vehicle partial looks like this:

<%= 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 -%>

车辆的部分用没有字段,因为没有构建 sale_vehicle.vehicle 对象。

The partial for the vehicle is rendered with no fields, because no sale_vehicle.vehicle object has been built.

我需要做的是构建嵌套对象与主对象一起(Cocoon当前不构建任何嵌套对象),但是如何最好地做到这一点呢?有什么方法可以从帮助程序代码中选择嵌套表单,以便可以构建它们?

What I need to do is thus build the nested object along with the main object (Cocoon does not currently build any nested objects), but how best to do this? Is there any way to select nested forms from the helper code so that these can be built?

Cocoon当前正在构建主要对象,如下所示:

Cocoon currently build the main object like this:

if  instance.collection?
    f.object.send(association).build
else
    f.object.send("build_#{association}")
end

如果我可以做下面的事情,它将使事情变得简单而美好,但是我不确定如何获得 f.children -有什么方法可以从父级表单生成器访问嵌套表单生成器?

If I could do something like the following, it would keep things nice and simple, but I'm not sure how to get f.children - is there any way to access nested form builders from the parent form builder?

f.children.each do |child|
    child.object.build
end

任何帮助我们都能正常工作的帮助,或建议动态构建这些对象的另一种方法。

Any help appreciated to get this working, or suggest another way of building these objects dynamically.

谢谢!

编辑:可能值得一提的是这个问题似乎与上述Cocoon宝石有​​关,也与Ryan Bates的 nested_form 宝石有关。针对Cocoon gem的问题#91 似乎与此问题相同,但dnagir((在这种情况下,委派对象的构建是不理想的,因为那样会导致其他形式的问题。

Probably worth mentioning that this question appears to be relevant to both the Cocoon gem mentioned above, and also Ryan Bates' nested_form gem. Issue #91 for the Cocoon gem appears to be the same problem as this one, but the workaround suggested by dnagir (delegating the building of the objects) is not ideal in this situation, as that will cause issues on other forms.

推荐答案

我可以在您的第二个嵌套表单中看到没有 link_to_add_association

I can see in your second nested form there is no link_to_add_association.

在茧中, link_to_add_association 在用户想要动态添加元素时进行新元素的构建。

Inside cocoon, the link_to_add_association does the building of a new element, for when a user wants to dynamically add it.

或者,您是说一次 sale_vehicle 已构建,它应该自动包含车辆吗?我以为用户必须选择要出售的车辆?

Or, are you implying that once a sale_vehicle is built, it should automatically contain a vehicle? I would assume a user would have to select the vehicle that is sold?

我有一个 test-project 演示了双重嵌套的形式:一个项目包含任务,该任务可以包含子任务。

I have a test-project that demonstrates the double nested forms: a project has tasks, which can have sub-tasks.

但是也许这与您想做的事情不够好?

But maybe that does not relate good enough to what you want to do?

您没有显示模型,但如果我正确理解了这种关系,则是

You do not show your models, but if I understand correctly the relations are

sale 
  has_many :sale_vehicles
sale_vehicle
  has_one :vehicle (has_many?)

因此,如果您有 sale_vehicle 可以具有车辆,那么我假设您的用户首先将 sale_vehicle 添加到销售,然后单击链接添加车辆。那就是茧能做得很好的地方。另一方面,如果您希望当茧动态创建 sale_vehicle 时,还创建一个 vehicle ,我

So if you have a sale_vehicle that can have a vehicle, then I would assume your user would first add the sale_vehicle to the sale and then, click the link to add the vehicle. That is what cocoon can do perfectly well. If, on the other hand, you want that when cocoon dynamically creates a sale_vehicle, a vehicle is also created, I see a few different options.

不能说我是这个的忠实粉丝,但是在您的 sale_vehicle after_initialize 回调中,您可以始终构建所需的车辆模型。

Can't say I am a real fan of this, but in the after_initialize callback of your sale_vehicle, you could always build the required vehicle model.

我在这里假设,由于您的 sale_Vehicle 无效/没有就不存在车辆模型,模型的责任是在构建时立即创建嵌套模型。

I am assuming here that since your sale_Vehicle is not valid/cannot exist without a vehicle model, it is the responsability of the model to create the nested model immediately on build.

请注意,对于每个对象创建,执行 after_initialize 。但这可能是一个快速解决方案。如果您拒绝空的嵌套模型,这应该可以实现。

Note that after_initialize is executed for each object creation, so this could be costly. But this could be a quick fix. If you reject empty nested models, this should work imho.

对于用户, sale_vehicle 似乎是一个对象,所以为什么不创建一个由sale_vehicle和车辆组成的装饰器,呈现为一种(嵌套的)形式,保存时,装饰者知道需要将其保存到正确的模型中。

For the user, the sale_vehicle and vehicle seem one object, so why not create a decorator, composed of a sale_vehicle and a vehicle, which is presented into one (nested) form and when saving this, the decorator knows it needs to be saved into the correct models.

注意:有不同的术语。装饰器通常只扩展带有某些视图方法的单个类,但也可以由不同模型组成。替代术语:演示者,视图模型。

Note: there are different terms for this. A decorator usually only extends a single class with some view-methods, but it could as well be a composition of different models. Alternativd terms: presenter, view-model.

无论如何,装饰器/演示者的功能是为用户抽象出底层数据模型。因此,无论出于何种原因,您都需要将单个实体拆分为两个数据库模型(例如,限制列的nr,保持模型可读性...),但对于用户而言,它仍然是单个实体。因此呈现为一体。

Anyway, the function of a decorator/presenter is to abstract away the underlying datamodel for your users. So, for whatever reason you needed to split up a single entity into two database models (e.g. to limit nr of columns, to keep models readable, ...) but for the user it still is a single entity. So "present" it as one.

不确定我是否喜欢这个,但这绝对是可能的。如果嵌套模型不是ActiveRecord :: Association,则已经支持该方法,因此添加它应该不难。但是,我对此增加感到犹豫。所有这些选项都使其变得更加复杂。

Not sure if I am a fan of this, but this definitely is a possibility. It is already supported if the "nested model" is not an ActiveRecord::Association, so this should not be too hard to add that. But I am hesitant about that addition. All those options make it more complicated.

子对象。这必须在 fields_for 之前进行,然后您就可以开始了。

Inside your partial just build the needed child object. This has to happen before the fields_for and then you are good to go. Something like

<% 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 -%>



结论



我个人非常喜欢装饰器方法,但可能会有点沉重。只需在渲染之前构建对象,然后调用 fields_for ,这样就可以确保至少有一个对象。

Conclusion

I personally really like the decorator approach, but it could be a bit heavy. Just build the object before you render call the fields_for, that way you are always sure there is at least one.

我很想听听您的想法。

希望这会有所帮助。

这篇关于Rails-动态构建深度嵌套的对象(Cocoon / nested_form)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆