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.
Let's take a look at the image of this goal.
"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.
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.
$ 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
$ 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.
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