【问题标题】:Triple-nested form with Rails form_with and strong parameters使用 Rails form_with 和强参数的三重嵌套表单
【发布时间】:2019-05-30 17:48:32
【问题描述】:

我正在运行 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 %>

产品控制器:

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

【问题讨论】:

  • 在f.fields_for :option_values这个表单中嵌套在主表单里面,但是应该嵌套在options_form里面
  • 谢谢,但f.fields_for :option_values 不是嵌套在f.fields_for :options 中吗?
  • 它是嵌套在 html 标记方面,但不是在 rails form_helper 方面。应该是options_form.fields_for :option_values
  • 另外,表单中没有 option_values 的 account_id 字段,但该属性已验证。
  • 好的,这是有道理的,但是当我添加 &lt;%= options_form.fields_for :option_values do |values_form| %&gt; 时,它不会在浏览器中呈现 values_form 字段。

标签: ruby-on-rails ruby forms nested-forms strong-parameters


【解决方案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

【讨论】:

    【解决方案2】:

    更新:

    因为我试图跟随 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 的唯一区别是在控制器中使用强参数,因此您只需像这样嵌套它们:

    产品控制器

    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
    

    选项控制器

    def option_params
         params.require(:option).permit(:account_id, :name, option_values_attributes [:id, :account_id, :name, :_destroy])
    end
    

    选项值控制器

    def option_value_params
         params.require(:option_value).permit(:account_id, :option_id, :name)
    end
    

    我不会在控制器中构建嵌套对象,而是使用 Railscast 剧集中的 Javascript 或在她的回答中建议的 Cocoon gem 或 Vasilisa 中的 Cocoon gem。

    只是想分享实际运行的代码,以防其他人遇到类似问题。我认为 Railscast 虽然很老,但仍然是 Rails 中嵌套表单的一个很好的介绍,但您只需要注意使用 form_with 和强参数所需的更改。非常感谢 Vasilisa 帮助我解决这个问题。

    关注Rails Nested Form Railscast 时需要注意的主要“陷阱”如下:

    • form_with 与旧的 rails form_tag 有一些不同的语法
    • 确保在创建表单块时没有任何拼写错误或名称问题,因为它们嵌套了两次
    • 与控制器中的嵌套参数相同,请注意语法和拼写错误
    • 请注意,Ryan Bates 正在演示的数据不是通过他正在构建的表单添加的,因此如果您想继续进行操作,则需要在控制台中创建一些数据
    • 使用强参数,您必须将 :_destroy 明确列为参数,以便他的“删除”复选框起作用

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-18
      • 1970-01-01
      • 1970-01-01
      • 2017-03-18
      • 2015-09-20
      相关资源
      最近更新 更多