・ Cloud9 Ubuntu Server Rails 6.0.0 ・ Ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
Suppose you have a Parent model and a Kid model with one-to-many relationships like this:
models/parent.rb
class Parent < ApplicationRecord
has_many :kids, dependent: :destroy
end
models/kid.rb
class Kid < ApplicationRecord
belongs_to :parent
end
Since the data of the child model is saved at the same time as the data of the parent model is saved, accepts_nested_attributes_for
To the Parent model.
models/parent.rb
class Parent < ApplicationRecord
has_many :kids, dependent: :destroy
accepts_nested_attributes_for :kids
end
This allows you to use nested forms that allow you to register the associated data in one form.
Next, in the Parents controller of the parent model, create an empty instance that receives the params sent from the form page.
It also creates an empty instance of the associated child model. Also in the strong parameter to receive the prams of the child model
kids_attributes: [:name, :age, :toy]
Is passing.
#### **`controllers/parents_controller.rb`**
```rb
class ParentsController < ApplicationController
#(Omitted)
def new
@parent = Parent.new
@parent.kids.build #Create an empty instance of the child model
end
def create
@parent = Parent.new(parent_params)
if @parent.save
redirect_to root_url
else
render :new
end
end
#(Omission)
private
def parent_params
#Allows you to receive parameters for child models
params.require(:parent).permit(
:name, :age, kids_attributes: [:name, :age, :toy]
)
end
end
erb:new.html.erb
<div class="container">
<div class="col-sm-10 col-sm-offset-1">
<h1 class="text-center">Parent registration</h1>
<%= form_with(model: @parent, local: true) do |f| %>
<div class="field form-group">
<%= f.label :name %>
<%= f.text_field :name, class: "form-control" %>
</div>
<div class="field form-group">
<%= f.label :age %>
<%= f.number_field :age, class: "form-control" %>
</div>
<!--Nested form to receive data for child models-->
<%= f.fields_for :kids do |kf| %>
<h1 class="text-center">Child registration</h1>
<div class="field form-group">
<%= kf.label :name %>
<%= kf.text_field :name, class: "form-control" %>
</div>
<div class="field form-group">
<%= kf.label :age %>
<%= kf.number_field :age, class: "form-control" %>
</div>
<div class="field form-group">
<%= kf.label :toy %>
<%= kf.text_field :toy, class: "form-control" %>
</div>
<% end %>
<div class="field form-group">
<%= f.submit "Register with the above contents", class: "btn btn-primary btn-lg btn-block" %>
</div>
<% end %>
</div>
</div>
It's hard to see because it contains the Bootstrap class, but the above `` `f.fields_for``` is the form that receives the data of the child model.
Although it is an input form for child models, by creating multiple empty instances, you can increase the number of input fields without increasing the form of view itself. For example, by changing the Parents controller as follows, one set of child model input forms can be displayed at the time of new registration, and two sets of child model input forms can be displayed at the time of update.
controllers/parents_controller.rb
class ParentsController < ApplicationController
#(Omission)
def new
@parent = Parent.new
@parent.kids.build #Create an empty instance of the child model
end
def create
@parent = Parent.new(parent_params)
if @parent.save
redirect_to root_url
else
render :new
end
end
#(Omission)
def edit
@parent = Parent.find(params[:id])
@parent.kids.build #Create an empty instance of the child model
end
def update
@parent = Parent.find(params[:id])
if @parent.update(parent_params)
redirect_to root_url
else
render :edit
end
end
def destroy
@parent = Parent.find(params[:id])
@parent.destroy
redirect_to root_url
end
private
def parent_params
params.require(:parent).permit(
:name, :age, kids_attributes: [:name, :age, :toy]
)
end
end
By creating an empty instance of the child model also in the edit action, "registered child model data" + 1 input form will be created at the time of update.
This makes it easier to increase the number of input forms than adding them dynamically with JS. (The bottleneck is that the number is limited ...)
If you want to add more forms, you can create more empty instances.
@parent.kids.build
From
#### **`n.times { @parent.kids.build }`**
By doing, you can generate n input forms.
However, as it is, empty data will be saved if the added form is not filled out.
To prevent this, pass the following Proc as the second argument of accepts_nested_attributes_for
added to the Parent model.
models/parent.rb
class Parent < ApplicationRecord
has_many :kids, dependent: :destroy
accepts_nested_attributes_for :kids, reject_if: lambda {|attributes| attributes['name'].blank?}
end
In the above case, if the name of the child model input form is empty, all other attributes (here, age, toy) will not be saved even if they are entered.
If you do not want to register if name or age is not entered, change as follows.
models/parent.rb
class Parent < ApplicationRecord
has_many :kids, dependent: :destroy
accepts_nested_attributes_for :kids, reject_if: lambda {|attributes| attributes['name'].blank? || attributes['age'].blank?}
end
In addition to the above, you can freely control the stored values by changing the validation to the child model.
In addition to this, you may want to delete only the data of the associated child model, but for details, see "Action View Form Helper" in the Rails guide. It will be helpful.
I'm Tatsuron, a beginner who is currently studying Ruby on Rails. I posted about the features that I got stuck for several days while creating my own application. If you have any mistakes or advice, please comment.
Recommended Posts