All threads / How can I add multiple dynmic nested fields in the same form?
Ask A Question

Notifications

You’re not receiving notifications from this thread.

How can I add multiple dynmic nested fields in the same form?

Scott Moore asked in Rails

I have a registration form that allows tournament directors to select which fields they will request when participants register for a tournament. I followed a video to add dynamic nested form fields into a form using stimulus JS and it works great.

I am trying to add a second group of fields in another section fo the form. the form is broken into section, basic info, contact info, competition info, etc and I would like to have the ability to add custom fields to each section.

In the basic section it works great. I can add and remove custom fields, but when I added it to the contact section, even with change nested-form to nested-contacts, it just puls in the fields I create in the basic section. I can add and remvoe from either section but it just shows them all in both sections.

Here is my form as it is now. I still have to add the other sections but was trying to get basic and contact custom fields working before adding the rest of the form:

<h4>Athlete Information</h4>
<%= form_with(model: @event, class: "shadow p-3 mb-3 rounded text-dark", local: true) do |f| %>
  <%= f.hidden_field :athlete_info_complete, value: 1 %>
  <%= f.hidden_field :next, value: 6 %>
  <div class="row club_container">
    <div class="col-12">
      <legend>
        Check all the boxes next to the information you would like to
        request from your tournament participants.  Each area will have an option 
        to add custom fields.
      </legend>
      <br><br>
      <h4>Basic Information</h4>
      <div class="form-group row">
        <div class="col-md-9 col-sm-12">
          <label class="main">First Name
            <%= f.check_box :first_name %>
            <span class="w3docs"></span>
          </label>
          <label class="main">Last Name
            <%= f.check_box :last_name %>
            <span class="w3docs"></span>
          </label>
          <label class="main">Date of birth
            <%= f.check_box :dob %>
            <span class="w3docs"></span>
          </label>
        </div>
      </div>
      <h4>Custom Fields</h4>
      <small>
        Enter the name of your custom field you plan to offer.
      </small>
      <br><br>

      <div data-controller="nested-form">
        <template data-target="nested-form.template">
          <%= f.fields_for :fields, Field.new, child_index: 'NEW_RECORD' do |field| %>
            <%= render 'events/forms/custom_fields_basic', f: field %>
          <% end %>
        </template>

        <%= f.fields_for :fields do |field| %>
          <%= render 'events/forms/custom_fields_basic', f: field %>
        <% end %>
        <div class="mb-3" data-target="nested-form.links">
          <%= link_to 'Add Custom Field', "#", class: "btn btn-outline-dark", data: { action: "click->nested-form#add_association" } %>
        </div>
      </div>

      <h4>Contact Information</h4>
      <div class="form-group row">
        <div class="col-md-9 col-sm-12">
          <label class="main">Address 1
            <%= f.check_box :address1 %>
            <span class="w3docs"></span>
          </label>
          <label class="main">Address 2
            <%= f.check_box :address2 %>
            <span class="w3docs"></span>
          </label>
          <label class="main">City
            <%= f.check_box :city %>
            <span class="w3docs"></span>
          </label>
        </div>
      </div>

      <div data-controller="nested-contacts">
        <template data-target="nested-contacts.template">
          <%= f.fields_for :fields, Field.new, child_index: 'NEW_RECORD' do |field| %>
            <%= render 'events/forms/custom_fields_contact', f: field %>
          <% end %>
        </template>

        <%= f.fields_for :fields do |field| %>
          <%= render 'events/forms/custom_fields_contact', f: field %>
        <% end %>
        <div class="mb-3" data-target="nested-contacts.links">
          <%= link_to 'Add Custom Field', "#", class: "btn btn-outline-dark", data: { action: "click->nested-contacts#add_association" } %>
        </div>
      </div>

      <div class="form-group row">
        <div class="col-md-9 col-sm-12"></div>
        <div class="col-md-3 col-sm-12">
          <button id="button_next" class="profile_btn align-middle" style="width:180px;">Finalize <span style="margin-top: 5px; font-size: 18px;"><i class="fas fa-long-arrow-alt-right"></i></span></button>
        </div>
      </div>
    </div>
  </div>
<% end %>

Here is the _custom_fields_basic.html.erb

<%= content_tag :div, class: 'nested-fields', data: { new_record: f.object.new_record? }  do %>
  <div class="form-group row">
    <div class="col-md-3 col-sm-12 col-form-label">
      <%= f.label :field_name %>
    </div>
    <div class="col-md-9 col-sm-12">
      <%= f.text_field :name, class: 'form-control' %>
      <small><%= link_to 'Remove', '#', data: { action: 'click->nested-form#remove_association' } %></small>
    </div>
  </div>
  <%= f.hidden_field :field_type, value: 'basic' %>
  <%= f.hidden_field :_destroy %>
<% end %>

This is the custom_fields_contact.html.erb file:

<%= content_tag :div, class: 'nested-contacts', data: { new_record: f.object.new_record? }  do %>
  <div class="form-group row">
    <div class="col-md-3 col-sm-12 col-form-label">
      <%= f.label :field_name %>
    </div>
    <div class="col-md-9 col-sm-12">
      <%= f.text_field :name, class: 'form-control' %>
      <small><%= link_to 'Remove', '#', data: { action: 'click->nested-contacts#remove_association' } %></small>
    </div>
  </div>
  <%= f.hidden_field :field_type, value: 'contact' %>
  <%= f.hidden_field :_destroy %>
<% end %>

Here is the nexted_form_controller.js

import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "links", "template" ]

  connect() {

  }

  add_association(event) {
    event.preventDefault()

    var content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime())
    this.linksTarget.insertAdjacentHTML('beforebegin', content)
  }

  remove_association(event) {
    event.preventDefault()

    let wrapper = event.target.closest(".nested-fields")
    if (wrapper.dataset.newRecord == "true") {
      wrapper.remove()
    } else {
      wrapper.querySelector("input[name*='_destroy']").value = 1
      wrapper.style.display = 'none'
    } 
  }
}

And the nested_contacts_controller.js:

import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "links", "template" ]

  connect() {

  }

  add_association(event) {
    event.preventDefault()

    var content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime())
    this.linksTarget.insertAdjacentHTML('beforebegin', content)
  }

  remove_association(event) {
    event.preventDefault()

    let wrapper = event.target.closest(".nested-contacts")
    if (wrapper.dataset.newRecord == "true") {
      wrapper.remove()
    } else {
      wrapper.querySelector("input[name*='_destroy']").value = 1
      wrapper.style.display = 'none'
    } 
  }
}

If there is way to do it with a single controller that would be great, but I'm ok with having separate controllers for each section if necessary.

Thanks

Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 70,078+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more. Icons by Icons8

    © 2023 GoRails, LLC. All rights reserved.