Rails - Dynamically build deeply nested objects (Cocoon / nested_form)

独自空忆成欢 提交于 2019-12-02 18:13:56

I can see in your second nested form there is no 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.

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?

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?)

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.

Use 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.

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.

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.

Use Decorator/Presenter

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.

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.

Allow cocoon to call a custom build method

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.

EDIT: The simplest fix

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

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.

I am interested to hear your thoughts.

Hope this helps.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!