My nested json payload looks like :
{
"id": 1,
"courses": [
{
"math": [
{
"name": "Math A",
"hours": 10,
"grade": "B"
},
{
"name": "Math B",
"hours": 20,
"grade": "A"
},
{
"name": "Math C",
"hours": 10,
"grade": "B"
}
],
"english": [
{
"number_of_students": 10,
"hours": 20,
"name": "Intro to English",
"grade": "B"
},
{
"number_of_students": 15,
"hours": 25,
"name": "English 101",
"grade": "A"
}
],
},
{}, {}, {}....
]
}
I need to loop through the available options to generate the form, then construct a nested payload as above.
<%= simple_form_for @courses_payload, course_path(id), method: :post do |f| %>
<% available_options.each do |option| %>
<%= f.simple_fields_for Course.new do |c| %>
<%= option.name %>
<% if option.name == "english" %>
<%= c.input : number_of_students %>
<% end %>
<%= c.input :hours %>
<%= c.input :grade %>
<% end %>
<% end %>
<% end %>
api.create(
params[:courses_payload].permit(
courses:[math: [:name, :hours, :grade], english: [:number_of_students, :hours, :name, :grade]]
)
However, I was not able to get all the nested fields into the payload. The form can only record grade field, and not other nested fields(name, hours, number_of_students) . Is there something wrong with my loop?
If I have only hash to work with, I'll use it for my form. But the form applies to models as well, maybe with a few tweaks.
<%
hash = {
"id": 1,
"courses": [
{
"math": [
{ "name": "Math A", "hours": 10, "grade": "B" },
{ "name": "Math B", "hours": 20, "grade": "A" },
{ "name": "Math C", "hours": 10, "grade": "B" }
],
"english": [
{ "number_of_students": 10, "hours": 20, "name": "Intro to English", "grade": "B" },
{ "number_of_students": 15, "hours": 25, "name": "English 101", "grade": "A" }
],
}
]
}
%>
The goal is to make our inputs name
attributes like this:
courses_payload[courses][0][english][0][number_of_students]
<%= simple_form_for :courses_payload, url: "/courses/#{hash[:id]}", method: :post do |f| %>
<% hash[:courses].each.with_index do |courses,i| %>
<% courses.each do |course, values| %>
<%= f.simple_fields_for :courses, index: i do |ff| %>
<%# values = [{:name=>"Math A", :hours=>10, :grade=>"B"}, {:name=>"Math B", :hours=>20, :grade=>"A"}, {:name=>"Math C", :hours=>10, :grade=>"B"}] %>
<% values.each.with_index do |value, k| %>
<%# course = 'math' %>
<%= ff.simple_fields_for course, index: k do |fff| %>
<%# value = {:name=>"Math A", :hours=>10, :grade=>"B"} %>
<% value.each do |input_name, input_value| %>
<%# input_name = "hours"; input_value = "10" %>
<%= fff.input input_name, input_html: { value: input_value } %> <%# <input value="10" class="string required" type="text" name="courses_payload[courses][0][math][0][hours]" id="courses_payload_courses_0_math_0_hours"> %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
To make the form submit an actual array is a bit tricky. Input name
attribute has to look like this:
courses_payload[courses][][english][][number_of_students]
We need to explicitly tell the form builder the index values are blank (nil works in some situations).
<%= simple_form_for :courses_payload, url: "/courses/#{hash[:id]}", method: :post do |f| %>
<% hash[:courses].each do |courses| %>
<% courses.each do |course, values| %>
<%= f.simple_fields_for 'courses', index: '' do |ff| %>
<% values.each do |value| %>
<% value.each do |input_name, input_value| %>
<%= ff.simple_fields_for course, index: '' do |fff| %>
<%= fff.input input_name, input_html: { value: input_value } %> <%# <input value="10" class="string required" type="text" name="courses_payload[courses][][math][][hours]" id="courses_payload_courses__math__hours"> %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
A couple of my other answers that involve arrays in forms: