経緯
いろいろなシステムを構築していると、グループを作成するときにメンバーも同時に作成したくなることがあります。
例えば、以下のようなモデルがあるとして、GroupのnewのときにMemberも作成することを考えてみます。
class Group < ActiveRecord::Base has_many :members end class Member < ActiveRecord::Base belongs_to :group end
簡単なやり方
gemのインストール
まず、簡単にフォームを扱うために、nested_formというgemを入れます。
特に、追加できるMemberの数を可変にすることがなければ、このgemは必要ありません。
Gemfile
gem 'nested_form'
$ bundle install
nested_formはViewで使える便利なHelperを用意してくれます。
Modelの修正
まずはModelを編集します。親のGroupに対して、
accepts_nested_attributes_for :members, allow_destroy: true
を追記してやりましょう。子モデルのフィールドを扱ってもいいよー、という設定です。
allow_destroyについては後ほど書きます。
Controllerの修正
app/controllers/groups_controller.rb
def new @group = Group.new @group.members.build # 追記 end
Groupを作成するとき、空のGroupを予め作っておくのと同様に、空のMemberを作成しておいてやります。
app/controllers/groups_controller.rb
def group_params
params.require(:group).permit(
:name,
members_attributes: [:name] # 追記
)
end
members_attributesはGroupに紐づくMemberのフィールドになります。
このmembers_attributesをGroup自体にぶっこんでやれば、自動的にMemberが作成されるようになります。
Viewの修正
app/controllers/views/groups/new.html.erb
<%= nested_form_for @group do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :members do |mf| %>
<%= mf.label :name %>
<%= mf.text_field :name %>
<%= mf.link_to_remove '駆逐してやる!' %>
<% end %>
<%= f.link_to_add '前方に20m級!' :members %>
<%= f.submit %>
<% end %>
ひとまず、実行してみると、「前方に20m級!」を押すと、20m級の巨人かどうかは分かりませんが、memberのフィールドが追加され、「駆逐してやる!」を押すと、そのフィールドが削除されることがわかると思います。1
こいつが、nested_formの便利機能です。
簡単な解説
Viewで、form_forの代わりにnested_form_forを使うことで、link_to_addやlink_to_removeのようにnested_formの便利関数を使えるようになりました。
名前からお分かりでしょうが、link_to_add '前方に20m級!', :membersはMemberのフィールドを追加する「前方に20m級!」というボタンを作成し、
link_to_remove '駆逐してやる!'はMemberを削除する「駆逐してやる!」というボタンを作ってくれます。
ちなみに、link_to_removeは先ほど、Modelのところで、少し触れたallow_destroy: trueがないと動きません。ご注意ください。
nested_formを使わないときはこれらの機能が使えないので、上記サンプルからは削除しましょう。
f.fields_for :membersはこのBlock内は@groupに紐付いたmemberのフィールドですよ!といっています。
出力されるHTMLを確認してみればわかりますが、この中のフィールドのnameにControllerで定義したmembers_attributesが含まれています。
mfという変数を使うこと以外は通常のform_forの中と同じように記述できます。
最初に2つ以上のフィールドを作っておきたい。
最初に作っておきたいフィールドの数を変更したいときはController内に記述した
@group.members.build
の記述を
2.times { @group.members.build }
のようにしてやればOKです。
逆に最初からMemberのフィールドを用意しない場合はこの記述自体を削除しましょう。
まとめ
以上のように意外と簡単に作成できるのでいろいろと活用していけると思います。
まとめると、
- 親Modelに子Modelのフィールドへのアクセス許可を与える
- Controllerで予め空の子Modelを作成してやる
- Controllerで
**_attributesというparamsをModelに渡すように実装する - Viewで
fields_forを使って子モデルのフィールドを作る
となります。
nested_forを使うと他にもいくつかカスタマイズも可能なので、それはまたの機会に。
- 進撃の巨人を見てから、大学時代の同級生のデカイやつの後頭部をドーンとやりたい衝動に駆られました。 ↩
コメントを残す