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

Add support many nested forms

Open NikitaNaumenko opened this issue 3 years ago • 10 comments

In one of my pet project, I've found the issue. That component doesn't work if you need to have more than one nested model form. I've fixed and also save the compatibility with one model nested form.

NikitaNaumenko avatar May 08 '21 06:05 NikitaNaumenko

I second this pull request. 😅 I'm currently using it on a few sites and it's working great.

brodyhoskins avatar Jun 28 '21 22:06 brodyhoskins

Thanks @NikitaNaumenko for the PR

Can you fix conflicts so I can review it?

guillaumebriday avatar Jun 29 '21 20:06 guillaumebriday

@guillaumebriday yep, I'll do it

NikitaNaumenko avatar Jun 30 '21 05:06 NikitaNaumenko

@guillaumebriday done!

NikitaNaumenko avatar Jul 11 '21 13:07 NikitaNaumenko

Hi.

Hi @guillaumebriday do you have an idea of when this may get merged?

Thanks

dp6ai avatar Aug 03 '21 07:08 dp6ai

Hey folks. Maybe I have this wrong, but it looks like you can use multiple nested forms now. I should probably have commented on this ticket, but I've left notes here: https://github.com/stimulus-components/stimulus-rails-nested-form/issues/8#issuecomment-942239527

Basically, just moving the controller attributes out of the form_with proper and into separate divs seems to do the trick. Just verified that it's working in an app I've working on, to make sure I wasn't going mad.

ferrisoxide avatar Oct 13 '21 12:10 ferrisoxide

I think the problem is this line const content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime().toString()) Since using /g will replace every ocurrence of NEW_RECORD then deep nested controller scopes will receive the parent timestamp id.

aquilarafa avatar Apr 19 '22 02:04 aquilarafa

I think the problem is this line const content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime().toString()) Since using /g will replace every ocurrence of NEW_RECORD then deep nested controller scopes will receive the parent timestamp id.

Allowing to customize the NEW_RECORD placeholder for each nested form builder worked for me.


  static values = {
        wrapperSelector: {
            type: String,
            default: '.nested-form-wrapper'
        },
        newRecordPlaceholder: {
            type: String,
            default: 'NEW_RECORD'
        },
    }

    add(e) {
        e.preventDefault()
        const regex = new RegExp(`${this.newRecordPlaceholderValue}`, 'g')
        const content = this.templateTarget.innerHTML.replace(regex, new Date().getTime().toString())
        this.targetTarget.insertAdjacentHTML('beforebegin', content)
    }

In my case the first form receives a child_index placeholder of "NEW_RECORD_1" and the deep nested receives "NEW_RECORD_2" for example.

aquilarafa avatar Apr 19 '22 02:04 aquilarafa

I think the problem is this line const content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime().toString()) Since using /g will replace every ocurrence of NEW_RECORD then deep nested controller scopes will receive the parent timestamp id.

Allowing to customize the NEW_RECORD placeholder for each nested form builder worked for me.


  static values = {
        wrapperSelector: {
            type: String,
            default: '.nested-form-wrapper'
        },
        newRecordPlaceholder: {
            type: String,
            default: 'NEW_RECORD'
        },
    }

    add(e) {
        e.preventDefault()
        const regex = new RegExp(`${this.newRecordPlaceholderValue}`, 'g')
        const content = this.templateTarget.innerHTML.replace(regex, new Date().getTime().toString())
        this.targetTarget.insertAdjacentHTML('beforebegin', content)
    }

In my case the first form receives a child_index placeholder of "NEW_RECORD_1" and the deep nested receives "NEW_RECORD_2" for example.

Using a custom stimulus value solves this issue but maybe a smarter way to set this id could bring a better experience. Any ideas?

aquilarafa avatar Apr 19 '22 03:04 aquilarafa

Can confirm @aquilarafa 's idea works for many layers of nested forms.

@ferrisoxide I think the issue is when you need to have a nested form that has 2 layers or more, the template in the second layer will be created with name value like this:

policy[agents_attributes][1692332505676][overrides_attributes][1692332505676][override_type]

Notice the same id for both parent and child records. This will make the form only submit the last item created in the 2nd layer, because all inputs in that layer will have the same name.

The correct template name value should be something like this:

policy[agents_attributes][1692332505676][overrides_attributes][NEW_OVERRIDE][override_type]

For a slightly "smarter" solution, I guess since we can get child_index value from the form builder:

# will return 'NEW_RECORD'
fields_for_form_builder.options[:child_index]

We can add a hidden field to the template, then figure out the given child index from the Stimulus controller?

planetaska avatar Aug 18 '23 04:08 planetaska