I am somehow ending up with Vue displaying both variants of a v-if
condtion, when it should be displaying only one.
The layout starts like this:
and after a drag operation looks like this, with the extra component ( a vuetify
v-chip
) being pushed into a new row because there isn’t enough room (there should be only 5 in a row):
I have checked the contents of the grid row that contains the data for these chips and confirmed that, after the drag, it still has only five columns, so there should be just 5 chips. The duplicated chip (the first ‘A’, the one that should not be there) has the outlined
variant and the correct one (the blue ‘A’) does not – this is the correct result of dragging the original outlined ‘A’ chip out of the grid. The Vue template section is:
<v-container id="startgrid" style="max-width: 240px; height: 250px; position: relative;">
<v-row v-for="(row,r) in startGrid" >
<template v-for="(chip,c) in row" :key="chip.id" >
<v-chip v-if="!chip.used" size="x-large" variant="outlined" class="copyable changeclass"
@dropped="drop"
@swapped="swap"
:data-keep-row = r
:data-keep-col = c
:data-letter = chip.name
:id=idString(0,r,c)
> {{ chip.name }}
</v-chip>
<v-chip v-else size="x-large"
:data-letter = chip.name
:id=idString(0,r,c)
color="blue"
> {{ chip.name }}
</v-chip>
</template>
</v-row>
</v-container>
To confirm that I really am seeing both conditions of the v-if
, I temporarily added the row and column numbers to the text of the blue variant but not to the outlined one, the result being (for a different letter being dragged):
Also (although not shown here) I temporarily added the length of the grid row to the chip name, to see how long ‘Vue’ thought it was when it was actually rendered. It was 5.
Looking at the elements in the browser debugger confirms what I am seeing, i.e. an extra component with opposing styles.
The drag/drop code manipulating the components (the code that provides the dropped
and swapped
events) is too involved to replicate here, so I am just wondering what sort of error might lead to this? How can chip.used
(the conditional value) be both true and false at the same time?
Edit
A suggested answer is that the :key
values may be duplicated. I have checked these and they are all defined uniquely – 1 to 25 for each cell of the 5 x 5 grid. Having said that, is there a way I can view these :key
values for each component in a browser inspector? I can see the element id’s but not any :key
values. More specifically, if I am copying/replacing an element (see code below), do I need to reset the :key
? If so, how?
I have also been asked to provide a reproducible example. Currently there are hundreds of code lines leading to this error, but I will try to arrange something smaller. In the meantime, the specific lines causing it are something like this:
function copyObject(source,dest) {
const parent2 = dest.parentNode;
const parent1 = source.parentNode;
const nextSib = source.nextSibling;
if (parent1 === null || parent2 === null)
throw 'null parent(s) in copying element'
// a copy needed to replace the one being copied and moved
const copyElement = source.cloneNode(true);
// move to new grid
parent2.replaceChild(source,dest);
// replaceChild() removes original , so replace the original with the copy
parent1.insertBefore(copyElement.el,nextSib);
}
2
Answers
@Tao's suggestion was in fact the answer. It is incorrect to try to manipulate the DOM when using Vue (or, I suppose , other reactive-type libraries). All I needed to do was to ignore the final DOM manipulation of my drag/drop library:
plus a simple change afterwards to copy the new images back into the data source, ready for Vue to render.
This is now so much more straightforward than trying to move the elements themselves. I am still using DOM manipulation for temporary changes (e.g. moving an copy image across the screen during a drag) so most of the rest of my library remains intact, and can still be used both for Vue and for plain JS.
Plus a bonus, from a comment I also learned about vue DevTools for Chrome - it looks great! Why didn't I find it sooner?
Most likely, your
:key
is not unique, leading to Vue being unable to detect which nodes to remove. Here is a playground reproducing the behavior.Could this be your issue?