Rails 6:动态嵌套表单:获取由fields_for和collection_select选择的嵌套资源的id.<



如果你正面临着类似的问题,你可能会发现阅读这篇文章很有趣:

  • 有人能解释collection_select给我清晰,简单的术语吗?

我试图实现嵌套表单与几个模型和动态添加的collection_select字段,基于这个伟大的教程https://www.driftingruby.com/episodes/nested-forms-from-scratch

遗憾的是,我目前有麻烦检索产品。Id从我的控制器适当地保存记录。

In my schema:

  • 一个配方有许多产品(尽管FormularProducts)有许多订单
  • 一个订单有许多OrderProducts(跟踪其数量)和许多Products(跟踪:product_id)
  • 一个OrderProduct属于一个Order,属于一个Product
  • 一个产品有许多公式,有许多订单,有许多公式产品和有许多订单产品

当我触发一个包含多个OrderProduct记录的提交时,只传递最后一个:product_id:

Started PATCH "/formulars/1/orders/53"
Parameters: 
{
"authenticity_token"=>"[FILTERED]",
"product_id"=>{"id"=>"7"},                         # <- :product_id of the last collection_select
"order"=>
{
"order_products_attributes"=>
{
"1612773831378"=>{"quantity"=>"1",         # <- each OrderProduct dynamically added
"_destroy"=>"false"},    # <-
"1612774917407"=>{"quantity"=>"6",         # <- frontend JS substitutes the associated 
"_destroy"=>"false"},    # <- objects' :object_id with a unique 
"1612774918874"=>{"quantity"=>"4",         # <- timestamp.
"_destroy"=>"false"}}},  # <- I need to access :product_id from here,
}                                            #    for each OrderProduct added
},
"commit"=>"Modifier ce(tte) Commande", 
"formular_id"=>"1",
"id"=>"53"                                         # <- Order's :id
}

字段是如何动态添加到表单的:

# app/views/orders/_form.html.erb
<%= form_with(model: [formular, order] do |form |%>                        # <- Form for an Order binded to a Formular (nested routes)
....
<%= link_to_add_product_row('Add an article', form, :order_products) %>  # <- Call a Helper method
<% end %>
# app/helpers/application_helper.rb
def link_to_add_product_row(name, f, association, **args)
new_object = f.object.send(association).klass.new     # <- create a new instance of the associated object
# f.object   == #<Order id: 53> in this context
# new_object == #<OrderProduct id: nil, order_id: nil, product_id: nil, quantity: 1>
id = new_object.object_id                             # <- get its unique :object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize, f: builder)    # 'orders/_order_product' is a partial shown below
end
link_to(name, '#', class: "add_fields " + args[:class], data: {id: id, fields: fields.gsub("n",'')})
end
# app/views/orders/_order_product.html.erb
<tr>
#
# I need this collection_select to:
#   - display the Product's name
#   - display only Products associated to the selected Formular
#   - pre-select the Product record, if set
#   - return the Product.id to the backend
#
<td><%= collection_select(:product_id, :id, @formular.products, :id, :name) %></td>
<td></td>
<td><%= f.number_field :quantity, hide_label: true %></td>
<td><%= f.submit %></td>
<td>
<%= f.hidden_field :_destroy, as: :hidden, method: :delete %>
<%= link_to 'Delete', '#', class: 'remove_order_product' %>
</td>
</tr>
$(document).on('turbolinks:load', function() {
$('form').on('click', '.add_fields', function(event) {
var regexp, time;
time   = new Date().getTime();
regexp = new RegExp($(this).data('id'), 'g');
$('.fields').append($(this).data('fields').replace(regexp, time));
return event.preventDefault();
});
});
# app/controllers/orders_controller.rb
# before_action callback executed on :create and :update
# goal is to retrieve OrderProducts from params, and associate
# them to the current instance of Order
def set_order_products
order_products_params = order_params.dig(:order, :order_products_attributes)
if order_products_params
order_products_params.each_value do |param|
product = Product.find(param['product_id'])
@order.order_products.build(product:  product,
quantity: param['quantity'])
end
end
end
# If OrderProducts params were like:
#
#   {"TIMESTAMP"=>{ 
#      "product_id"=>"42",         # <- this parameter is missing
#      "quantity"=>"3",
#      "_destroy"=>"false"}}
#
# then I'd be able to identify to which product this OrderProduct is associated to

如果你想要模式代码,请告诉我。


对如何实现这个目标有什么想法吗?我试过添加hidden_fields,调整collection_select,使用selectoptions_from_collection_for_select/options_for_select;但是我必须承认我迷路了(我可能把我的模式过于复杂了)。

TL_DR:如何从Order的控制器内动态生成的field_for(:order_products)中检索Product的ID ?

From有人可以向我解释collection_select清晰,简单的术语吗?https://stackoverflow.com/a/8908298/11509906

zquares说:

关于使用form_for,同样用非常简单的术语来说,对于form_for中的所有标签,例如:f.t text_field,你不需要提供第一个(object)参数。这是取自form_for语法。

我把collection_select改成:

<%= collection_select(:product_id, :id, @formular.products, :id, :name) %>
# Parameters: {"authenticity_token"=>"[FILTERED]", "product_id"=>{"id"=>"7"}, "order"=>{"order_products_attributes"=>{"1612782399659"=>{"quantity"=>"3", "_destroy"=>"false"}, "1612782403591"=>{"quantity"=>"5", "_destroy"=>"false"}}}, "commit"=>"Modifier ce(tte) Commande", "formular_id"=>"1", "id"=>"53"}

:

<%= f.collection_select(:product_id, @formular.products, :id, :name) %>
# Parameters: {"authenticity_token"=>"[FILTERED]", "order"=>{"order_products_attributes"=>{"1612782084184"=>{"product_id"=>"3", "quantity"=>"3", "_destroy"=>"false"}, "1612782085172"=>{"product_id"=>"33", "quantity"=>"-3", "_destroy"=>"false"}}}, "commit"=>"Modifier ce(tte) Commande", "formular_id"=>"1", "id"=>"53"}

现在我可以在Order的控制器

中获取相关产品