我正在运行Rails 6.0.0.rc1,但无法使三重嵌套表单正常工作。我有products
,有options
,有option_values
。因此,一个产品可能有一个名为“颜色”的选项和一个名为“红色”的选项值。我想在“产品”表单的classic nested form中创建所有这些文件。
该表格有效,我可以保存带有期权的产品,但不能保存提交时的期权价值。我不确定为什么在尝试将fields_for
选项值嵌入fields_for
选项内时为何不起作用。
我在这里做错了什么?我觉得我缺少明显的东西,但无法弄清楚。 (可能不相关,但是请注意,我需要将每个对象的范围限制为account_id
,而我的用户has_one :account
是隐藏字段的原因。)
这是我的产品型号:
class Product < ApplicationRecord
belongs_to :account
has_many :options, dependent: :destroy
accepts_nested_attributes_for :options, allow_destroy: true
validates :account_id, presence: true
validates :name, presence: true
end
选项模型:
class Option < ApplicationRecord
belongs_to :account
belongs_to :product
has_many :option_values, dependent: :destroy
accepts_nested_attributes_for :option_values, allow_destroy: true
validates :account_id, presence: true
validates :name, presence: true
end
OptionValue模型:
class OptionValue < ApplicationRecord
belongs_to :account
belongs_to :option
validates :account_id, presence: true
validates :name, presence: true
end
这是产品表单:
<%= form_with(model: product, local: true) do |f| %>
<%= f.fields_for :options do |options_form| %>
<fieldset class='form-group'>
<%= options_form.hidden_field :account_id, value: current_user.account.id %>
<%= options_form.label :name, 'Option' %>
<%= options_form.text_field :name, class: 'form-control' %>
</fieldset>
<%= f.fields_for :option_values do |values_form| %>
<fieldset class='form-group'>
<%= values_form.label :name, 'Value' %>
<%= values_form.text_field :name, class: 'form-control' %>
</fieldset>
<% end %>
<% end %>
<% end %>
ProductsController:
class ProductsController < ApplicationController
def new
@product = Product.new
@product.options.build
end
def create
@account = current_user.account
@product = @account.products.build(product_params)
respond_to do |format|
if @product.save
format.html { redirect_to @product, notice: 'Product was successfully created.' }
else
format.html { render :new }
end
end
end
private
def product_params
params.require(:product).permit(
:account_id, :name,
options_attributes: [
:id, :account_id, :name, :_destroy,
option_values_attributes:[:id, :account_id, :name, :_destroy ]
]
)
end
end
答案 0 :(得分:1)
首先,您需要更改表单以将option_values
嵌套在option
内,并将account_id
字段添加到选项值:
<%= form_with(model: product, local: true) do |f| %>
<%= f.fields_for :options do |options_form| %>
<fieldset class='form-group'>
<%= options_form.hidden_field :account_id, value: current_user.account.id %>
<%= options_form.label :name, 'Option' %>
<%= options_form.text_field :name, class: 'form-control' %>
</fieldset>
<%= options_form.fields_for :option_values do |values_form| %>
<fieldset class='form-group'>
<%= values_form.hidden_field :account_id, value: current_user.account.id %>
<%= values_form.label :name, 'Value' %>
<%= values_form.text_field :name, class: 'form-control' %>
</fieldset>
<% end %>
<% end %>
<% end %>
此外,您还需要在控制器中构建嵌套记录。另一种选择是通过javascript动态构建它们(例如,查看cocoon gem)。要构建3个选项,每个选项具有3个值:
def new
@account = current_user.account
# it is better to create associated product
@product = @account.products.new
3.times do
option = @product.options.build
3.times { option.option_values.build }
end
end
答案 1 :(得分:0)
更新:
因为我尝试遵循this Nested Form Railscast,所以最大的问题是我没有意识到Ryan Bates使用的版本是“编辑”,而不是“新”,因此我添加了产品,通过控制台输入选项和值,并使用该代码处理表单:
_form.html.erb
<%= f.fields_for :options do |builder| %>
<%= render 'option_fields', f: builder %>
<% end %>
_option_fields.html.erb
<fieldset class='form-group'>
<%= f.hidden_field :account_id, value: current_user.account.id %>
<%= f.label :name, 'Option' %>
<%= f.text_field :name, class: 'form-control' %>
<br>
<%= f.check_box :_destroy, class: 'form-check-input' %>
<%= f.label :_destroy, 'Remove Option' %>
<small id="optionHelp" class="form-text text-muted">
(e.g. "Size" or "Color")
</small>
<%= f.fields_for :option_values do |builder| %>
<%= render 'option_value_fields', f: builder %>
<% end %>
</fieldset>
_option_value_fields.html.erb
<fieldset class='form-group'>
<%= f.hidden_field :account_id, value: current_user.account.id %>
<%= f.label :name, 'Value' %>
<%= f.text_field :name, class: 'form-control' %>
<br>
<%= f.check_box :_destroy, class: 'form-check-input' %>
<%= f.label :_destroy, 'Remove Value' %>
<small id="optionValueHelp" class="form-text text-muted">
(e.g. "Small, Medium, Large" or "Red, Green, Blue")
</small>
</fieldset>
此外,与Railscast的唯一区别是在控制器中使用了强大的参数,因此您只需要像这样嵌套它们:
ProductsController
def product_params
params.require(:product).permit(:account_id, :name, options_attributes [:id, :account_id, :name, :_destroy, option_values_attributes: [:id, :account_id, :name, :_destroy]])
end
OptionsController
def option_params
params.require(:option).permit(:account_id, :name, option_values_attributes [:id, :account_id, :name, :_destroy])
end
OptionValuesController
def option_value_params
params.require(:option_value).permit(:account_id, :option_id, :name)
end
与其在控制器中构建嵌套对象,不如在Railscast情节中使用Javascript或在答案中建议使用Cocoon gem,如Vasilisa来实现。
只想共享实际上最终可以工作的代码,以防其他人遇到类似问题。我认为Railscast尽管很老,但仍然是Rails中嵌套表单的很好的介绍,但是您只需要了解使用form_with和strong参数所需的更改。非常感谢Vasilisa帮助我解决了这个问题。
遵循Rails Nested Form Railscast时需要注意的主要“陷阱”是这样的:
:_destroy
作为参数,以便其“删除”复选框起作用