stimulus-rails-nested-form icon indicating copy to clipboard operation
stimulus-rails-nested-form copied to clipboard

Having a double nested form?

Open Sabrasse opened this issue 2 years ago • 1 comments

Hi! I'm new to Stimulus, and I have a little issue trying to use the nested form component.

I have a survey model with many questions, and my question model has many answers. I want to be able to create a survey, and add both questions & answers on the same view.

It works okay with a level 1 nesting (for the questions) but, I'm have a hard time adding the answer logic to it. When I'm adding an answer, it duplicates the creation of a question, I know it has to come from the way the target are set-up, but I can't find a way to have both actions being separated (a button to create a question, and one to create an answer).

I followed up this tutorial to do so, but he does not seems to encounter this issue.

Here is my initial _form :

`<%= simple_form_for ([@survey]), data: { controller: 'nested-form', nested_form_wrapper_selector_value: '.nested-form-wrapper' } do |f| %>
  <template data-nested-form-target="template">
    <%= f.simple_fields_for :questions, Question.new, child_index: 'NEW_RECORD' do |question| %>
      <%= render "question_fields", f: question %>
    <% end %>
  </template>

  <%= f.fields_for :questions do |question| %>
    <%= render "question_fields", f: question %>
  <% end %>

  <!-- Inserted elements will be injected before that target. -->
  <div data-nested-form-target="target">
    <%= f.button :submit, "Add question", data: {action:"nested-form#add"} %>
  </div>

  <%= f.submit 'Create survey' %>
<% end %>`

My _question partial :


<div class="nested-form-wrapper" data-new-record="<%= f.object.new_record? %>">
  <%= f.input :body, label: "Question" %>
  <%= f.button :submit, "Delete question", data: {action:"nested-form#remove"} %>
  <%= f.hidden_field :_destroy %>
</div>

<template data-nested-form-target="template">
  <%= f.simple_fields_for :answers, Answer.new, child_index: 'NEW_RECORD' do |answer| %>
    <%= render "answer_fields", f: answer %>
  <% end %>
</template>

  <%= f.fields_for :answers do |answer| %>
    <%= render "answer_fields", f: answer %>
  <% end %>

  <!-- Inserted elements will be injected before that target. -->
  <div data-nested-form-target="target"></div>

  <%= f.button :submit, "Add answer", data: {action:"nested-form#add"} %>

And my _answer partial :

<div class="nested-form-wrapper" data-new-record="<%= f.object.new_record? %>">
  <%= f.input :body, label: "Answer" %>
  <%= f.button :submit, "Delete answer", data: {action:"nested-form#remove"} %>
  <%= f.hidden_field :_destroy %>
</div>

If anybody had this issue before, it would love to know how you dealt with it, thanks!

Sabrasse avatar Feb 19 '23 08:02 Sabrasse

@kobaltz's tutorial that you linked to has a source download and if you inspect it, you'll see he is not using this project. Instead, he simply defined his own stimulus controller for the same purpose:

// app/assets/javascripts/controllers/nested_form_controller.js
import { Controller } from "stimulus"

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

  add_association(event) {
    event.preventDefault()
    var content = this.templateTarget.innerHTML.replace(/TEMPLATE_RECORD/g, new Date().valueOf())
    this.add_itemTarget.insertAdjacentHTML('beforebegin', content)
  }

  remove_association(event) {
    event.preventDefault()
    let item = event.target.closest(".nested-fields")
    item.querySelector("input[name*='_destroy']").value = 1
    item.style.display = 'none'
  }
}

As a result, I wouldn't say it's safe to assume this component will work the same way, re: multi-nesting

searls avatar Apr 07 '24 12:04 searls