[Rails] Add / Remove Forms (cocoon)

When creating a web application with Rails, I wanted to add an input form, so I summarized the procedure below. If you use a gem called "cocoon", you can easily create it, so this time we will proceed with that method.

Image of goal

Let's take a look at the image of this goal.

Image from Gyazo

agenda

  1. Table design
  2. Introduction of cocoon
  3. Modeling
  4. Create controller
  5. Create view

Table design

"Recipe" is the parent table, and "Recipe Ingredient" and "How To Make" are prepared as the child tables. The parent table and child table here have a one-to-many relationship. image.png

Introduced cocoon

In order to install cocoon, jQuery needs to be installed.

Gemfile


gem 'cocoon'
gem "jquery-rails"
$ bundle install

application.js


//= require jquery
//= require rails-ujs
//= require turbolinks
//= require_tree .
//= require cocoon

Added // = require jquery and // = require cocoon. The above order seems to be important.

Modeling

$ rails g model Recipe user:references title:string catchcopy:text no_of_dish:string image:string
$ rails g model RecipeIngredient recipe:references ing_name:string quantity:string
$ rails g model HowToMake recipe:references explanation:text process_image:string order_no:integer

Perform migration

$ rails db:migrate

recipe.rb


class Recipe < ApplicationRecord
  belongs_to :user
  has_many :recipe_ingredients, dependent: :destroy
  has_many :how_to_makes, dependent: :destroy
  accepts_nested_attributes_for :recipe_ingredients, :how_to_makes, allow_destroy: true
end

recipe_ingredient.rb


class RecipeIngredient < ApplicationRecord
  belongs_to :recipe
end

how_to_make.rb


class HowToMake < ApplicationRecord
  belongs_to :recipe
end

Since "recipe", "recipe_ingredients" and "how_to_makes" have a one-to-many relationship, use has_many.

accepts_nested_attributes_for By using accepts_nested_attributes_for, the data of the specified model can be included in the parameter as an array. (I will explain below.) In other words, the data of the "recipe", "recipe_ingredients" and "how_to_makes" models can be saved together.

Reference: https://qiita.com/seimiyajun/items/dff057b3eb40434d5c27

dependent: :destroy If the class with dependent:: destroy is deleted, the instance of the model with dependent:: destroy is also deleted.

In this case If Recipe is deleted, the instances of RecipeIngredients and HowToMake are also deleted.

Reference: https://qiita.com/eitches/items/1ad419dc705f807735e0

Controller creation

$ rails g controller recipes

Edit in controller

recipes_controller.rb


class RecipesController < ApplicationController

  def new
    @recipe = Recipe.new
    @recipe_ingredients = @recipe.recipe_ingredients.build ##Parent model.Child model.buildでChild modelのインスタンス作成
    @how_to_makes = @recipe.how_to_makes.build
  end

  def create
    @recipe = Recipe.new(recipe_params)
    if @recipe.save
      redirect_to root_path
    else
      render :new
    end
  end
  
  private
  
  def recipe_params
    params.require(:recipe).permit(:title, :catchcopy, :no_of_dish, :image, 
                                  recipe_ingredients_attributes:[:ing_name, :quantity, :_destroy], 
                                  how_to_makes_attributes:[:explanation, :process_image, :order_no, :_destroy])
  end
end

Recipe_ingredients model specified by accepts_nested_attributes_for It can be added and sent together as recipe_ingredients_attributes: []. The same applies to how_to_makes_attributes: [].

Enter _destroy to accept parameters for deletion.

View creation

Since it will be long, the parts not related to this form are omitted.

ruby:recipes/new.html.erb


<div class="recipe-post">
  <%= form_with(model: @recipe, local: true) do |f| %>
     
    <div class="recipe-ingredients">
      <div class="mx-auto">
        <%= f.fields_for :recipe_ingredients do |t| %>
          <%= render "recipes/recipe_ingredient_fields", f: t %>
        <% end %>
      </div>
      
      <div id="detail-association-insertion-point"></div>
      
      <div class="col-10 mx-auto mt-2">
        <%= link_to_add_association "+Add form", f, :recipe_ingredients,
        class: "btn btn-secondary btn-block",
        data: {
        association_insertion_node: '#detail-association-insertion-point',
        association_insertion_method: 'after'
        }%>
      </div>
    </div>

  <% end %>
</div>

Then, I will explain below.

<%= f.fields_for :recipe_ingredients do |t| %>
  <%= render "recipes/recipe_ingredient_fields", f: t %>
<% end %>

fields_for allows you to edit different models (in this case RecipeIngredient) within form_with. With render, jump torecipe_ingredient_fields (file name)and describe the contents of the form to be added dynamically here. (Explained below.)

<%= link_to_add_association "+Add form", f, :recipe_ingredients,
  class: "btn btn-secondary btn-block",
  data: {
  association_insertion_node: '#detail-association-insertion-point',
  association_insertion_method: 'after'
  }%>

The form is added by link_to_add_association.

association_insertion_node: '#detail-association-insertion-point' association_insertion_method: 'after' Specify the display position of the form by. It is assigned to <div id =" detail-association-insertion-point "> </div>.

ruby:_recipe_ingredient_fields.html.erb


<div class="nested-fields">
  <div class="row mx-auto">
    <div class="col-5"><%= f.text_field :ing_name, class: "form-control", placeholder: "material" %></div>
    <div class="offset-1 col-2"><%= f.text_field :quantity, class: "form-control", placeholder: "Quantity" %></div>
    <div class="offset-1 col-1 px-0 w-auto">
      <%= link_to_remove_association "Delete", f, class: "btn btn-secondary btn-block" %>
    </div>
  </div>
</div>

The file name is fixed as _model name_fields.html.erb. You need to set a class called nested_fields in this partial to enclose the form contents. You can delete a form with link_to_remove_association.

reference: https://qiita.com/matata0623/items/8868a7fcb6ec0817d064 https://qiita.com/obmshk/items/0e942177d8a44091bf09

If you have any supplements or corrections, please comment! !!

Recommended Posts

[Rails] Add / Remove Forms (cocoon)
[Ruby on Rails] Add / Remove Columns
Add binding.pry (rails)
[Rails 6] cocoon_ introduction
[Rails] Add column to devise
Add a search function in Rails.
[Rails] How to add new pages
Remove "assets" and "turbolinks" in "Rails6".
[Rails] Add strong parameters to devise
Add Not null constraint after Rails
Add / remove watermark to Java PowerPoint
[Rails 6] cocoon_ Add id and data attributes to the form to be added