MORYLAB;

エンジニアの卵がソースコードやライフログを綴るブログ。

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

1対多のリレーションを持つモデルを作る必要があったので、備忘録がてらその作り方をまとめようかと。 cocoon公式のReadmeを参考に作ってみた。

https://github.com/nathanvda/cocoon


サンプルの概要

今回は、1対多の関係性を持つモデルを作成し、1つの入力画面からデータを入力できるようにする。 モデルの概要は以下の通り。

[Project] (1)
| name | description |

[Task] (多)
| description | done |

gemの導入

Gemfileに以下を追記する。

gem 'cocoon'


Rails5.1以降はJQueryを公式サポートしていないため、別途"JQuery-rails"を導入する必要がある。該当する場合はGemfileにこれも追記。

gem 'jquery-rails'


そしていつものbundle install。

bundle install


Javascriptのロード設定
--assets/javascripts/application.js

//= require jquery
//= require cocoon


モデル

モデルを作成していく。
ProjectモデルはScaffoldを使用して作成する。

rails g scaffold Project name:string description:string


続いて、Taskのモデルも作成する。
1つのProjectが複数のTaskを持つ。

rails g model Task description:string done:boolean peoject:belongs_to


これらのモデルを以下のように関連付ける。
--models/project.rb

class Project < ApplicationRecord
  has_many :tasks, inverse_of: :project
  accepts_nested_attributes_for :tasks, allow_destroy: true
end


--models/task.rb

class Task < ApplicationRecord
  belongs_to :project
end


いつものお約束。Migration。

rails db:migrate


コントローラー

ネストされたモデルを削除するには、_destroyというvirtual attributeが使用される。
ここでパラメータ関連の設定を記述する。
--controllers/projects_controller.rb

def project_params
  params.require(:project).permit(:name, :description, tasks_attributes: [:id, :description, :done, :_destroy])
end


ビュー

ビューの設定を行う。
ReadmeではHAMLを使っているが、自分はHAMLを使用していないのでerbで書いた。
--views/_form.html.erb

<%= form_for @project do |f| %>
<div class="field">
  <%= f.label :name %>
  <br/>
  <%= f.text_field :name %>
</div>
<div class="field">
  <%= f.label :description %>
  <br/>
  <%= f.text_field :description %>
</div>

<h3>Tasks</h3>
<div id="tasks">
  <%= f.fields_for :tasks do |task| %>
    <%= render 'task_fields', f: task %>
  <% end %>
  <div class="links">
    <%= link_to_add_association 'add task', f, :tasks %>
  </div>
</div>
<%= f.submit %>
<% end %>


Taskのフォーム部分を別ファイルに記述する。
増減させたい項目にクラス"nested-fields"をつける。
--views/_task_fields.html.erb

<div class="nested-fields">
  <div class="field">
    <%= f.label :description %>
    <br/>
    <%= f.text_field :description %>
  </div>
  <div class="field">
    <%= f.check_box :done %>
    <%= f.label :done %>
  </div>
  <%= link_to_remove_association "remove task", f %>
</div>


これでおわり。
最後に、どんな感じで動くかを。

デモ

f:id:morrrry:20171105223209g:plain