Skip to content
Advertisement

Vue: Removing item from v-for list leaves behind canvas item

This is a hard one to phrase in a title, but pretty easy to explain with some setup.

I have a list of components. Each component in the list looks like the following:

| [Canvas Preview] Name      (Delete button) |

Each component has a small canvas element that is used to display a preview of the item, It’s name, and a delete button to remove it from the list. The list that the v-for is iterating through is stored in Vuex.

Using letters to represent different preview images, a list might look like this:

| [W] Item 1      (Delete button) |
| [X] Item 2      (Delete button) |
| [Y] Item 3      (Delete button) |
| [Z] Item 4      (Delete button) |

When the delete button is pressed, the appropriate item is spliced out of the list and the list updates. The names update, and clicking on them results in the correct item being selected. The problem is that the previews stay in their same position. For example, if I deleted Item 2 (with preview X) from the list above I would have the following list:

| [W] Item 1      (Delete button) |
| [X] Item 3      (Delete button) |
| [Y] Item 4      (Delete button) |

The previews are WXY, when they should be WYZ since X was deleted. Basically the canvases are remaining in the same order, and one is just being deleted off the end, no matter where the item was deleted from.

I can think of a couple hackey ways of redrawing all the canvases when an item is deleted, but I was wondering if there was a better solution.

EDIT: Here’s the code that deletes the item

Delete button on item emits event to list component

<button @click="deleteAsset">Delete</button>

deleteAsset(event){
    event.stopPropagation();
    this.isRenaming = false;
    this.$emit('deleteAsset', this.asset);
},

Wrapper then dispatched Vuex action

deleteAsset(asset){
    this.$store.dispatch('GameData/deleteAsset', {category: asset.category_ID, id: asset.ID});
    this.updateAsset();
},

Vuex action and mutation

//Action
deleteAsset({commit}, {category, id}){
    commit('deleteAsset', {category, id})
}

//Mutation
deleteAsset: (state, {category, id}) => {
    let curList = getList();
    let hasFound = false;

    for (let i = 0; !hasFound && i < curList.length; i++){
        if (curList[i].ID == id){
            curList.splice(i, 1);
            hasFound = true;
        }
    }
}

Here’s a screenshot of the issue: Screenshot of Vue issue

The rest of the data is correct, as I added a quick test function to print the current ID and data on click and everything prints correctly. It appears it’s just the canvas element that’s not changing.

EDIT 2: The vue code

//List
<Asset
    ref="assets"
    v-for="asset in selectedList"
    :key="asset.cat_ID"
    :asset="asset"
    :defaultIcon="selected_category.icon"
    @deleteAsset="deleteAsset"
    @selectAsset="selectAsset"/>

//Asset code
<template>
    <div ref="asset" class="asset" :class="{selected : isSelected}" @click="selectAsset">
        <div class="leftFloat">
            <canvas v-show="hasThumb" class="thumbnail" ref="thumbNail" width="20" height="20">Test</canvas>
            <img v-if="!hasThumb" class="thumbnail assetIcon" :src="require(`@/${defaultIcon}.svg`)" />
            <div v-if="isRenaming">
                <input ref="renameText" v-model="asset.name" type="text" />
            </div>
            <div v-else>{{asset.name}}</div>
        </div>
        <div class="rightFloat">
            <button class="rightButton" @click="deleteAsset">
                <img class="rightIcon" src="@/assets/trash.svg" />
            </button>
        </div>
    </div>
</template>

Advertisement

Answer

Assuming cat_ID can be assigned to more than one asset, you’ll run into problems using it as a key as Vue will not be able to detect list changes if the cat_ID is repeated in any of your selectedList assets.

Since your assets have an ID property, you should use that in your key

<Asset
  ref="assets"
  v-for="asset in selectedList"
  :key="asset.ID"
  :asset="asset"
  :defaultIcon="selected_category.icon"
  @deleteAsset="deleteAsset"
  @selectAsset="selectAsset"/>

See key

The key special attribute is primarily used as a hint for Vue’s virtual DOM algorithm to identify VNodes when diffing the new list of nodes against the old list. Without keys, Vue uses an algorithm that minimizes element movement and tries to patch/reuse elements of the same type in-place as much as possible. With keys, it will reorder elements based on the order change of keys, and elements with keys that are no longer present will always be removed/destroyed.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement