use nesteds_form in rails

在 rails 中,如果要在同個表單下一併儲存關聯性物件常常會用到 nested_forms 的功能,以下介紹。

Model

  • 把需要一併新增的關聯物件設定 accepts_nested_attributes_for , controller 就可以使用 addresses_attributes params ,一併將address新增。
class Person < ApplicationRecord
  has_many :addresses, inverse_of: :person

  accepts_nested_attributes_for :addresses
end

class Address < ApplicationRecord
  belongs_to :person
end

Form

<%= form_with model: @person do |form| %>
  Addresses:
  <ul>
    <%= form.fields_for :addresses do |addresses_form| %>
      <li>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>

        <%= addresses_form.label :street %>
        <%= addresses_form.text_field :street %>
        ...
      </li>
    <% end %>
  </ul>
<% end %>

Params

{
  'person' => {
    'name' => 'John Doe',
    'addresses_attributes' => {
      '0' => {
        'kind' => 'Home',
        'street' => '221b Baker Street'
      },
      '1' => {
        'kind' => 'Office',
        'street' => '31 Spooner Street'
      }
    }
  }
}

controller

params.require(:person).permit(:name, address_attributes: [:id, :kind, :street])

這樣就可以在新增 person 時一併新增 address 了!可以多多使用這種方式省下不必要的 model 。

另外, accepts_nested_attributes_for 還提供了 allow_destroy: true 方式能夠刪除 address。


加入 allow_destroy: true

model

----
 accepts_nested_attributes_for :addresses, allow_destroy: true
----

form

    <%= form.fields_for :addresses do |addresses_form| %>
      <li>
        <%= addresses_form.check_box :_destroy %>
      </li>
    <% end %>

此時觀察 params 就會發現多了一個 _destroy 的 params,因此加入 controller 的 permit 即可。

params.require(:person).permit(:name, address_attributes: [:id, :kind, :street, :_destroy])

參考來源

https://guides.rubyonrails.org/form_helpers.html#nested-forms