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:
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.