简体   繁体   中英

Vue.js doesn't re-render components with v-for

I'm trying to make a multiple text input component. There's a parent Vue component to manage individual fields and a component for a single input.

The parent component renders the child components via a v-for which is bound to an array of objects.

data() {
    return {
        lastItemId: 0,
        items: [{ id: this.lastItemId, value: '', showDeleteBtn: false}]
    }
},

Template:

<div>
    <input-item v-for='(item, index) in items'
                :key="item.id"
                :default-data="item"
                @itemAdded="addItemAfter(index)"></input-item>
</div>

Whenever the user starts typing in the last field in the list I'm emitting an event to add a new text field after it.

addItemAfter(index) {
    if (index == this.items.length - 1) {
        this.items.push({
            id: ++this.lastItemId,
            value: '',
            showDeleteBtn: false
        });
    }
}

This works just fine. However, I also need to update the item at the current index to display the delete button near that field. Whatever I do, Vue doesn't re-render that component, unless I set an object with a different ID to that index - which is not what I want.

Things I have tried:

this.items[index].showDeleteBtn = true;

let item = this.items[index];
item.showDeleteBtn = true;
this.$set(this.items, index, item);

let item = this.items[index];
item.showDeleteBtn = true;
this.items.splice(index, 1, item);

this.$set(this.items[index], 'showDeleteBtn', true);

Update

This is the most important in this problem part of the child component:

<button class="btn text-danger" v-show="showDeleteBtn" @click.prevent="removeItem">
    <i class="glyphicon glyphicon-remove"></i>
</button>

// ....................................

props: ['defaultData'],

data() {
    return {
        itemId: this.defaultData.id,
        item: this.defaultData.value,
        showDeleteBtn: this.defaultData.showDeleteBtn
    }
},

The reason the delete button is not showing up when you update the item is that the value of the showDeleteBtn property of your child component is not updating.

That value is not updating because the Vue instance properties set in the data method are only set once on initialization. So, showDeleteBtn is only ever set once in the data method:

showDeleteBtn: this.defaultData.showDeleteBtn

When you update the showDeleteBtn property of the item being bound as the defaultData prop, that changes the value of the defaultData object in the child component. However, that does not automatically update the showDeleteBtn property in the child component.


The simplest solution would be to make showDeleteBtn a computed property instead of a property set by the data method:

computed: {
  showDeleteBtn() {
    return this.defaultData.showDeleteBtn;
  }
}

This explicitly tells the Vue instance to update the showDeleteBtn value with the value of this.defaultData.showDeleteBtn if it ever changes.


Another solution would be to make showDeleteBtn a prop:

props: { defaultData: Object, showDeleteBtn: Boolean },

And bind the value of item.showDeleteBtn to that prop from the parent component:

<input-item 
  v-for='(item, index) in items'
  :key="item.id"
  :default-data="item"
  :show-delete-btn="item.showDeleteBtn"
  @itemAdded="addItemAfter(index)"
/>

This way, any changes to the value of item.showDeleteBtn will be directly reflected in the value of the showDeleteBtn prop of the child component.

This way also makes it a little clearer from the parent scope how changing that property on the item is going to affect the child component.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM