[Rails 6] How to create a dynamic form input screen using cocoon

Introduction

In this article, the goal is to create the following input form. Image from Gyazo

Overview

Creating a function to save recipes and ingredients required for recipes together in the DB

Table structure

Since the recipe and the ingredients of the recipe are in a parent-child relationship, the table structure is as follows. Parent: recipes Child: Ingredients for recipe (recipe_ingredients)

Implementation

We will carry out in the following order.

    1. Introduction of jquery
  1. Introduction of cocoon
    1. Creating a model
  2. Creating a controller
  3. Create view

1. 1. Introduction of jquery

Install jquery to be able to use cocoon with rails6.

$ yarn add jquery 

Edit config / webpack / environment.js

config/webpack/environment.js


const { environment } = require('@rails/webpacker')

#Postscript from here
const webpack = require('webpack')
environment.plugins.prepend('Provide',
    new webpack.ProvidePlugin({
        $: 'jquery/src/jquery',
        jQuery: 'jquery/src/jquery'
    })
)
#Postscript up to here

module.exports = environment

2. Introduction of cocoon

Introduction of gem

Gemfile


gem 'cocoon'
$ bundle install

Add library

$ yarn add github:nathanvda/cocoon#c24ba53

After execution, it is OK if you can clear the following two items. ・ App / assets / javascripts / cocoon.js has been created -The following description has been added to package.json

package.json


"cocoon": "github:nathanvda/cocoon#c24ba53"

Finally, add the following contents to app / javascriptspacks / application.js

app/javascriptspacks/application.js


require('jquery')
import "cocoon";

3. 3. Creating a model

The description that is not related to the implementation contents this time is omitted.

Creating a model

$ rails g model Recipe
$ rails g model RecipeIngredient

Edit migration file

class CreateRecipes < ActiveRecord::Migration[6.0]
  def change
    create_table :recipes do |t|
      t.string     :name,     null: false
      t.timestamps
    end
  end
end
class CreateRecipeIngredients < ActiveRecord::Migration[6.0]
  def change
    create_table :recipe_ingredients do |t|
      t.references :recipe,            null: false, foreign_key: true
      t.integer    :ingredient_id,     null: false
      t.integer    :quantity,          null: false
      t.timestamps
    end
  end
end

Perform migration

$ rails db:migrate

Association settings

recipe.rb


class Recipe < ApplicationRecord
  has_many :recipe_ingredients, dependent: :destroy
  accepts_nested_attributes_for :recipe_ingredients
end

recipe_ingredient.rb


class RecipeIngredient < ApplicationRecord
  belongs_to :recipe
end

accepts_nested_attributes_for The data of the specified model can be included in the parameter as an array. In other words, you will be able to save the data for both the recipe and recipe_ingredients models together.

4. Creating a controller

Creating a controller

$ rails g controller recipes new create

Edit the contents of the controller

recipes_controller.rb


class RecipesController < ApplicationController
  def new
    @recipe = Recipe.new
    @recipe_ingredients = @recipe.recipe_ingredients.build
  end

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

  private

  def recipe_params
    params.require(:recipe).permit(:name, recipe_ingredients_attributes: [:id, :ingredient_id, :quantity, :_destroy])
  end
end

Recipe_ingredient model specified by accepts_nested_attributes_for, I added it to params as recipe_ingredients_attributes: [] and sent it.

5. Create view

As with the model, the description that is not related to the implementation contents this time is omitted.

ruby:recipes/new.html.erb


<%= form_with model: @recipe, url: '/recipes', method: :post, local: true do |f| %>

  <!--Recipe name-->
  <%= f.text_area :name %>

  <!--Ingredient input field-->
  <%= f.fields_for :recipe_ingredients do |t| %>
    <%= render "recipes/recipe_ingredient_fields", f: t %>
  <% end %>

  <!--Ingredient addition button-->
  <%= link_to_add_association "add to", f, :recipe_ingredients %>
<% end %>

fields_for You will be able to edit different models within form_with.

ruby:recipes/_recipe_ingredient_fields.html.erb


<div class="nested-fields">
    <%= f.collection_select(:ingredient_id, {}, :id, :name, {}) %>
    <%= f.number_field :quantity %>
    <div>Pieces</div>
    <%= link_to_remove_association "Delete", f %>
</div>

The range enclosed by the div tag specified by the nested-fields class is the area to be added / deleted.

Pay attention to the partial template name to render. If it is not "_child model_fields.html.erb", an error will occur.

Thank you for your hard work. With the above, I think that you can create a dynamic input form.

reference

Introduction of cocoon in Rails 6 Set up a cocoon gem that can implement nested forms concisely in a webpack environment

About creating dynamic input form [Rails] How to save multiple data in parent-child relationship table at the same time using cocoon

About fields_for How to use fields_for well

Recommended Posts

[Rails 6] How to create a dynamic form input screen using cocoon
[Rails] How to create a graph using lazy_high_charts
[Swift5] How to create a splash screen
[rails] How to create a partial template
How to easily create a pull-down in Rails
[Rails] How to create a Twitter share button
How to create a query using variables in GraphQL [Using Ruby on Rails]
[Rails] How to create a signed URL for CloudFront
A memo to simply create a form using only HTML and CSS in Rails 6
How to create an Excel form using a template file with Spring MVC
How to create a method
[Rails] How to install a decorator using gem draper
How to create a form to select a date from the calendar
I want to create a form to select the [Rails] category
Preparing to create a Rails application
[Java] How to create a folder
How to make a splash screen
How to create a jar file or war file using the jar command
[Rails 5] How to display the password change screen when using devise
How to execute a contract using web3j
How to sort a List using Comparator
[Rails] How to upload images using Carrierwave
How to insert a video in Rails
How to create a Maven repository for 2020
How to implement image posting using rails
[Rails] How to handle data using enum
How to implement a slideshow using slick in Rails (one by one & multiple by one)
How to build a Ruby on Rails environment using Docker (for Docker beginners)
[Docker] How to create a virtual environment for Rails and Nuxt.js apps
How to create a validator that allows only input to any one field
[Rails] How to create a table, add a column, and change the column type
Rails: How to write a rake task nicely
How to create a database for H2 Database anywhere
[Rails] How to write when making a subquery
[Rails] rails new to create a database with PostgreSQL
How to delete a controller etc. using a command
[Ethereum] How to execute a contract using web3j-Part 2-
How to create pagination for a "kaminari" array
How to create a class that inherits class information
How to create a theme in Liferay 7 / DXP
[Rails] How to upload multiple images using Carrierwave
[1st] How to create a Spring-MVC framework project
How to implement a like feature in Rails
How to generate a primary key using @GeneratedValue
Rails6.0 ~ How to create an eco-friendly development environment
How to display the result of form input
How to create hierarchical category data using ancestry
How to make a follow function in Rails
How to store data simultaneously in a model associated with a nested form (Rails 6.0.0)
[Spring sample code included] How to create a form and how to get multiple records
[Introduction] Try to create a Ruby on Rails application
How to create a Java environment in just 3 seconds
How to implement a like feature in Ajax in Rails
How to delete a new_record object built with Rails
Reasons to include ActiveModel :: Model to create a Form object
How to create a JDBC URL (Oracle Database, Thin)
How to create a Spring Boot project in IntelliJ
[Spring Boot] How to create a project (for beginners)
How to manually generate a JWT with Rails Knock
A memorandum when trying to create a GUI using JavaFX
How to delete custom Adapter elements using a custom model