deeply nested simple_form and simple fields

565 Views Asked by At

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?

1

There are 1 best solutions below

0
On BEST ANSWER

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:

https://stackoverflow.com/a/71688643/207090

https://stackoverflow.com/a/71921385/207090