LoginSignup
1
0

More than 1 year has passed since last update.

【Rails6】Cocoonの導入(環境構築から)

Posted at

概要

オリジナルアプリのレシピ共有サイトを作る中で

  • レシピの材料を複数カラムにまとめて保存したい
  • レシピの材料を追加する動的フォームを実装したい  

という目的にあったcocoonというgemの導入の流れを説明します。
公式サイト
参考にしたサイト

全体の流れ

  1. JQueryの導入
  2. cocoon(gem)の導入
  3. MVCの導入

1.JQueryの導入

  • ターミナルでyarn add jqueryを実行
ターミナル
$ yarn add jquery

注意:OSにHomebrewをインストールしていない人はyarn initを先に実行する。

  • environment.jsに下記記載
config/webpack/environment.js
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')

environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    jquery: 'jquery',
  })
)

module.exports = environment
  • application.jsに下記を追記
javascript/packs/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
// ここから
require("jquery")
//ここまで
  • application.html.erbを確認
views/layout/application.html.erb
<!--省略-->
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<!--私の場合はすでに書かれていたのでこのままにしておきました-->
<!--省略-->

2.Cocoon(gem)の導入

  • gemfileに記載
gemfile
# 私は一番下に記載しました
# bundle installからの再起動も忘れずに
gem 'cocoon'
  • yarn addする
ターミナル
$ yarn add @nathanvda/cocoon
  • application.jsに記載
javascript/packs/application.js
require("@nathanvda/cocoon")

import "cocoon"でも動くようです)

3.MVCの導入

テーブルのマイグレーションファイルなどは記載していません。
titleカラムがあるレシピテーブルとrecipe_idnameカラムがあるイングレディエントテーブルを作成しました。

  • ルーティング
config/routes.rb
Rails.application.routes.draw do
  root "recipes#index"
  resources :recipes, only: [:index, :new, :create]
end
  • 親モデル
app/models/recipe.rb
class Recipe < ApplicationRecord
  has_many :ingredients, inverse_of: :recipe

  validates :title, presence: true

  accepts_nested_attributes_for :ingredients, allow_destroy: true, reject_if: :all_blank
end

inverse_ofを記載する理由
--公式の本文--
When saving nested items, theoretically the parent is not yet saved on validation, so rails needs help to know the link between relations. There are two ways: either declare the belongs_to as optional: false, but the cleanest way is to specify the inverse_of: on the has_many. That is why we write : has_many :tasks, inverse_of: :project
--翻訳--
ネストされたアイテムを保存するとき、理論的には親モデル側はバリデーションで保存されない。railsでは二つのリレーションを明らかにする必要がある。これには2つのやり方があり、一つはbelongs_to側(子モデル側)にoptional: falseを記載するやり方。もう一つはhas_many側(親モデル側)にinverse_ofを記載するやり方で、こちらの方がクリーンに記載できる。

allow_destroy: trueを記載する事でフォームを削除できる

reject_if: :all_blankを記載する事で全て空の場合拒否する

  • 子モデル
app/models/ingredient.rb
class Ingredient < ApplicationRecord
  belongs_to :recipe

  validates :name, presence: true
end

  • コントローラー
app/controllers/recipes/controller.rb
class RecipesController < ApplicationController
  def new
    @recipe = Recipe.new
  end

  def create
    @recipe = Recipe.new(recipe_params)
    if @recipe.valid?
      @recipe.save
      redirect_to root_path
    else
      render :new
    end
  end

  private
  def recipe_params
    params.require(:recipe).permit(:title, ingredients_attributes: [:id, :recipe_id, :name, :_destroy])
  end
end

ingredients_attributesの中にある:id :_destroyは必須

  • トップページ
    まだ一覧表示やエラーハンドリングは実施してない
app/views/recipes/index.html.erb
<%= link_to "レシピの新規作成", new_recipe_path %>
  • フォーム入力画面
app/views/recipes/new.html.erb
<%= form_with model: @recipe, url: recipes_path, local: true do |f| %>
  <div class="field">
    <%= f.label :title %>
    <br/>
    <%= f.text_field :title %>
  </div> 
  <h3>材料</h3>
  
  <div id='ingredients'>
    <%= f.fields_for :ingredients do |ingredient| %>
      <%= render 'ingredient_fields', :f => ingredient %>
    <% end %>
    <div class='links'>
      <%= link_to_add_association 'add ingredient', f, :ingredients %>
    </div>
  </div>
  <%= f.submit 'Save' %>

<% end %> 
  • 部分レイアウト
app/views/recipes/_ingredient_fields.html.erb
<div class="nested-fields">
    <div class='nested-fields'>
    <div class="field">
      <%= f.label :name %>
      <%= f.text_field :name %>
    </div>
    <%= link_to_remove_association "remove ingredient", f %>
  </div>
</div>

実装中に困ったこと

  • コクーンを導入したのに動的フォームの追加がうまくいかない
    |これはJQueryが正しく導入できてないことが問題であることが多い
  • 二つのテーブルに保存するのでフォームオブジェクトを使ってみたがうまくいかなかった
    |cocoonを使った実装では@recipe.saveの時に二つのテーブルに保存できているので、フォームオブジェクトは使用しなくても良い。

以上になります。誤字脱字、情報が違うなどありましたら教えていただけると幸いです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0