In this article, the goal is to create the following input form.
Creating a function to save recipes and ingredients required for recipes together in the DB
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)
We will carry out in the following order.
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
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";
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.
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.
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.
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