目的
gemのcocoonを使って親子孫の複数テーブル&複数データを同時保存。
https://qiita.com/hitochan/items/5a45a95e644492d66160
こちらの記事で親子孫関係を作ることができます!
さらに私は子が複数、その子が持つ孫を複数にする場合を想定。
自分がやっていてさっぱりわからなかったので、メモとして残す。
前提
https://qiita.com/hitochan/items/5a45a95e644492d66160
こちらの記事を参考にcocoonで親子孫を同時送信することを理解している。
自分は筋トレ記録アプリを作成中で
親events
子menus
孫menu_sets
というモデルの関係になっている。
使い方
Cocoonは2つのヘルパー関数を定義している。
① link_to_add_association
② link_to_remove_association
ざっくり言うと
①フォームを追加
②フォームを削除
https://i.gyazo.com/81032117760b22757c77d404e7050deb.gif
今自分が作っているもので途中なので汚いですが、イメージとしてはこんな感じです。
実装例
### メニュー追加ボタン
eventに今日はこんな筋トレをしたという投稿をして
その投稿の中に筋トレメニュー(menu)
さらにそのメニューをどのくらいの重さで何回何セットしたのか(menu_set)
を投稿。
ここではビューの部分を中心に触れる。
<div class="NewRecord" id="NewRecord">
<div class="NewRecord_Box">
<h1>トレーニングを記録する</h1>
<div class="NewRecord_Box_Form">
<%= render 'form', path: events_path, method: :post, event: @event %>
</div>
</div>
</div>
↑まずは新規投稿画面でトレーニングを記録する。
<div class="text-right">
<%= link_to_add_association 'メニュー追加', f, :menus,
class: 'btn btn-success',
data: {
association_insertion_node: '#detail-association-insertion-point',
association_insertion_method: 'append' }
%>
</div>
<div class="form-group" id='detail-association-insertion-point'>
<%= f.fields_for :menus do |menu| %>
<%= render 'menu_fields', f: menu %>
<% end %>
</div>
↑フォームの中身を部分テンプレートを使う。
この中の link_to_add_association が筋トレメニュー追加ボタンとなる。(緑のメニュ追加ボタン)
link_to_add_association 4つのパラメータがある。
name: the text to show in the link
f: the form builder
association: the name of the association (plural) of which a new instance needs to be added (symbol or string).
html_options:
自分はなんのこっちゃという感じなので、つまり・・・
name:リンクに表示するテキスト
⇨ここでいう'メニュー追加'
f:フォームビルダー
⇨そのまま f
association:新しいインスタンスを追加する必要がある関連(複数)の名前(シンボルまたは文字列)。
⇨eventはhas_manyでmenusとアソシエーションを組んでいるので、:menus
html_options:追加のhtml-options
ここでメニュー追加ボタンを押した時に、どうする?というオプションを決めることができる。
data-association-insertion-node : the jquery selector of the node as string, or a function that takes the link_to_add_association node as the parameter and returns a node. Default: parent node
data-association-insertion-method : jquery method that inserts the new data. before, after, append, prepend, etc. Default: before
??え??
**data-association-insertion-node:**は追加ボタンが押されたら、データをここに差し込む
**data-association-insertion-method:**はその追加分を、どこに差し込むか。(それが前なのか、後ろなのか。。。)
<div class="form-group" id='detail-association-insertion-point'>
<%= f.fields_for :menus do |menu| %>
<%= render 'menu_fields', f: menu %>
<% end %>
</div>
今回はid='detail-association-insertion-point'で括っている、このdiv要素を追加する。
'append'なのでそこに追加する。
※cocoonでは動的なフォーム追加を行う場合、このように部分テンプレートを使って実装する必要がある。ファイル名も〇〇_fieldsにしなくてはいけない
### セット追加ボタン
理論的には今メニューセットボタンを作った一連の流れをもう一度やればいい。
<div class="nested-fields">
<%= f.hidden_field :id %>
<%= f.collection_select :name, f.object.selectable_menus, :name, :name, prompt: "何をしましたか?", class: 'form-control' %>
<%= link_to_add_association 'セット追加', f, :menu_sets,
class: 'btn btn-success',
data: {
data_association_insertion_traversal: 'closest',
association_insertion_method: 'append' }
%>
<%= link_to_remove_association '削除', f, class: 'btn btn-default' %>
<div id="menu_set-association-insertion-point">
<%= f.fields_for :menu_sets do |menu_set| %>
<%= render 'menu_set_fields', f: menu_set %>
<% end %>
</div>
</div>
ポイントはここの
data_association_insertion_traversal: 'closest'
closestで指定することでセットの親である、メニューに紐づいたセットが追加できる。
data-association-insertion-traversal : the jquery traversal method to allow node selection relative to the link. closest, next, children, etc. Default: absolute selection
### 削除ボタン
既に登場していますが、これをつけてあげることで追加したものが削除できるようになります。
<%= link_to_remove_association '削除', f, class: 'btn btn-default' %>
終わりに
学習を初めたばかりですが、自分でこうやってまとめることで理解が定着すると思います。
例え間違っていてもこのようにアウトプットすることは非常に重要だなと痛感しました。まだまだ理解が浅く至らない点あるとは思いますがご指摘いただければと思います。
参考文献
https://qiita.com/hitochan/items/5a45a95e644492d66160
https://rails.densan-labs.net/form/relation_register_form.html
https://github.com/nathanvda/cocoon