pymarco
pymarco

Reputation: 8195

Vue.js - Nested field array refreshes with incorrect data

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

Upvotes: 2

Views: 465

Answers (1)

Bert
Bert

Reputation: 82449

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.

Upvotes: 2

Related Questions