Reputation: 268
I looked at related questions on Stack Overflow, but I am at a loss trying to figure this one out.
To elucidate,
User
, A
and B
.A
contains, let's say, 3 records with the fields p1_name
, p1_score
, p2_name
and p2_score
for each record. So, A
looks like-----------------------------------------------------------
id
|
p1_name
|
p1_score
|
p2_name
|
p2_score
id
|
p1_name
|
p1_score
|
p2_name
|
p2_score
id
|
p1_name
|
p1_score
|
p2_name
|
p2_score
-----------------------------------------------------------
B
is the model where the the User
submits his guesses for what p1_name
and p2_name
from A
scored. So, B
looks like:---------------------------------------------------------------------------------
id
|
user
|
a_id
|
p1_name
|
p1_score_guess
|
p2_name
|
p2_score_guess
id
|
user
|
a_id
|
p1_name
|
p1_score_guess
|
p2_name
|
p2_score_guess
id
|
user
|
a_id
|
p1_name
|
p1_score_guess
|
p2_name
|
p2_score_guess
---------------------------------------------------------------------------------
B
belongs_to
A
as each record in B
is uniquely identified by user
and a_id
put together.I need to make a form for model B
, handled by the new
action for its controller, using the view views/B/new.html.erb
.
This needs to be a single form that shows 3 rows each containing:
p1_name
from model A
(as text)p1_score_guess
for model B
(as number field)p2_name
from model B
(as text)p2_score_guess
for model B
(as number field)Upon submission using one submit button, the above data for each of the 3 records needs to be submitted to the controller, and multiple records of B
be created.
I have not encountered this kind of requirement in Rails 4.2.5 before, and am not sure how to go about it. I have been trying to do this since two days, first using simple_form, which did not seem to support this (or I am not able to make one), and then the Rails Forms.
It's become tricky for me because I have to show the p1_name
and p2_name
from model A
in the view under the form as well as use it also, to populate model B
. B
s controller needs to take in both p1_name
, p2_name
and p1_score_guess
, p2_score_guess
. Since a_id
is different for each row, that needs to be taken in as well (further making it impossible for me to figure this one out).
Upvotes: 2
Views: 664
Reputation: 288
how about:
<%= form_for :bs, url: bs_path do |f| %>
<% @as.each do |a| %>
<%= f.fields_for "[#{a.id}]", B.new do |afields| %>
<%= a.p1_name %>
<%= afields.text_field :p1_score_guess %>
<%= a.p2_name %>
<%= afields.text_field :p2_score_guess %>
<% end -%>
<% end -%>
<%= f.submit 'save' %>
<% end -%>
And in Controller you iterate through params[:a_s]
def create
bs_params.each do |a_id, b_attributes|
B.create b_attributes.merge(a_id: a_id, user: current_user)
end
end
with
def bs_params
params.require(:bs).permit(allowed_a_s)
end
def allowed_a_s
Hash[a_ids.map {|id| [id.to_s, [:p1_score_guess, :p2_score_guess]]}]
end
def a_ids
A.pluck(:id)
end
Just to show you my testcase - I assigned for @as:
@as = [A.new(id: 1, p1_name: 'p11', p2_name: 'p12'), A.new(id: 2, p1_name: 'p21', p2_name: 'p22'), A.new(id: 3, p1_name: 'p31', p2_name: 'p32')]
And set as a_ids static:
def a_ids
%w[1 2 3]
end
and got on a debugger for bs_params with inputs 1, 2, 3, 4, 5, 6 for guesses:
=> {
"1"=>{"p1_score_guess"=>"1", "p2_score_guess"=>"2"},
"2"=>{"p1_score_guess"=>"3", "p2_score_guess"=>"4"},
"3"=>{"p1_score_guess"=>"5", "p2_score_guess"=>"6"}
}
Update: Since above code does not check validations, I try again (this time not tested - sorry):
<%= form_for :bs, url: bs_path do |f| %>
<% @bs.each do |a, b| %>
<%= f.fields_for "[#{a.id}]", b do |afields| %>
<%= a.p1_name %>
<%= afields.text_field :p1_score_guess %>
<%= a.p2_name %>
<%= afields.text_field :p2_score_guess %>
<% end -%>
<% end -%>
<%= f.submit 'save' %>
<% end -%>
And in controller do:
def new
@bs = Hash[a_ids.map { |a_id| [a_id, B.new] }]
end
def create
@bs = {}
bs_params.each do |a_id, b_attributes|
@bs[a_id] = B.new b_attributes.merge(a_id: a_id, user: current_user)
end
@bs.values.each(&:valid?) # to ensure, all errors are assigned - and not finding one will stop to check for any other
if @bs.values.all?(&:valid?)
@bs.values.each &:save
redirect_to root_path
else
render :new
end
end
and bs_params, allowod_a_s and a_ids as above
Upvotes: 2