2

I am trying to create a Vue page that contains nested field collections. I.e. Parent form and repeatable child forms.

It works with the exception that when deleting a child form, the template renders incorrectly

Please see the fiddle example that I created - https://jsfiddle.net/c4marcus/1mu2oceb/8/

The sample data contains basic information for The Beatles. If you click the trash can next to "Ringo" then mistakenly "George" will disappear and not "Ringo".

However, when you click submit the correct data is being saved (see screenshot below).

I feel like the problem must lie with the MemberFormset vue component's remove method which is triggered by clicking the trash can button.

remove: function(index) {
    this.members.splice(index, 1)
    this.$emit('input', this.members)
},

Once spliced, the template should render the array of forms with the new data.

<div class="form-group" v-for="(member, index) in members">
    <member-form :model="member"
                 :index="index"
                 @input="update(index, $event)">

        <div slot="trash">
            <button type="button"
                    class="btn btn-default margin-top-md"
                    @click="remove(index)">

                <i class="fa fa-trash"></i>
            </button>
        </div>

    </member-form>

    <hr v-if="separator(index)" />
</div>

enter image description here

3
  • 2
    Rather than giving the whole code in your fiddle, you should try to get a minimal reproducible example that reproduce your error. I'll still give it a try though Commented Aug 17, 2017 at 18:06
  • Thank you, I'll try to trim it down soon as a moment frees up. Commented Aug 17, 2017 at 18:14
  • 1
    FWIW, having a fiddle makes a world of difference! Thank you for making it. Commented Aug 17, 2017 at 18:48

1 Answer 1

2

The main issue appears to be here:

 <member-form :model="member"
              :index="index"
              @input="update(index, $event)">

You need to provide a key for the custom component included in your loop. In this case you are not directly iterating on the custom component, but providing a key to Vue helps it determine it's strategy to update the DOM. To that end I added an id to each member object

members: [
  {name: 'John', id: 1},
  {name: 'Ringo', id: 2},
  {name: 'Paul', id: 3},
  {name: 'George', id: 4}
]

and updated the template to this:

 <member-form :model="member"
              :index="index"
              @input="update(index, $event)"
              :key="member.id">

One more thing, as pointed out in the comments, your add method needs to be updated to add a new id value.

add: function () {
  const newId = Math.max(this.members.map(m => m.id)) + 1
  this.members.push({name: null, id: newId})
},

Now your DOM is properly updated after a delete.

Here is the updated fiddle.

I noted a few things looking over some of the code that look like they fall into some Vue caveats. This code for example:

update: function(index, value) {
  this.members[index] = value
  this.$emit('input', this.members)
},

Looks like it would fall into Vue's array detection caveat. And althought it might not be causing issues right now, potentially might in the future.

Sign up to request clarification or add additional context in comments.

5 Comments

It is not if you use the add member button and then delete again. Because when add is triggered no id is given (maybe this.members.push({ id: generateId() } would work)
@UlysseBN True, because a new id is not generated in his add method. I went ahead and updated that. There are a few other things that look like they call into weird areas, but I didn't try to fix all the code.
My approach for generating ID would have been to keep a running index but this is ok too, +1
Wow, thank you so much Bert! It hadn't crossed my mind to check the v-for docs, just wasn't expecting it.
And thank you both for the additional help too! I'm trying to rapidly learn Vue and still have lots of detail to soak up.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.