Posted at

cocoonで1対多対1のフォームを作ろうとしたときの話


概要

cocoonで関連モデルも含めたformを作ることができるが、1対多対1のような、関連先の更に先の関連まで含めたフォームを作るためのTipsがあまりなかった&うまくできない部分もあったので、ここに残しておきます。


cocoonで1対多対1のフォームを作る

コントローラー、ビュー、モデルそれぞれに分けて書いていきます。


モデル

class Parent < ApplicationRecord

has_many :children, dependent: :restrict_with_exception, inverse_of: :parent
accepts_nested_attributes_for :children, allow_destroy: true
end

class Child < ApplicationRecord

belongs_to :parent
has_one :grand_son, dependent: restrict_with_exception, inverse_of: :child
accepts_nested_attributes_for :grand_son, allow_destroy: true
end

class GrandSon < ApplicationRecord

belongs_to :child
end

親側にaccepts_nested_attributes_forをつければいいだけです。

cocoonのREADMEに書いてあるとおりですね。


コントローラー

class ParentsController < ApplicationController

# 詳細は省略
def create
@parent = Parent.create!(parent_params)
end

private

def parent_params
params.require(:parent).permit(
:name,
children_attributes: [
:id,
:name,
:_destroy,
{
grand_son_attributes: [
:id,
:name,
],
},
])
end
end

以上のような形で、ストロングパラメーターにxxx_attributesを孫のモデルまで一気に書いてしまいます。

これで一気に孫のモデルまで登録できるようになります。


ビュー

問題はビューです。

このWikiによれば、force_non_association_createtrueにしてしまえば、1個しか追加できないようなのですが、子と孫が1対1の関係の場合は、複数個追加できてしまいます。

これでは狙ったとおりにできないので、自分は以下のように対応しました。


application.js

$('#children')

.on('cocoon:after-insert', function () {
$(this).css('cssText', 'display: none !important');
});

上記のようにすることで、追加ボタンを押した際に、その追加ボタンを消してしまうことができます。

ただ、すごくかっこ悪いので、いい方法があれば、ご教示いいただけますと幸いです。