skip to Main Content

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:

enter image description here

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

enter image description here

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

enter image description here

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


  1. Chosen as BEST ANSWER

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

    function copyObject(source,dest) {
    
      if (Draggable.options.reactive)
        // nothing to do here
            return;
    
      const parent2 = dest.parentNode;
      const parent1 = source.parentNode;
      const nextSib = source.nextSibling;
      .....
      .....
    }
    

    plus a simple change afterwards to copy the new images back into the data source, ready for Vue to render.

    trackGrid[toRow][toCol].type = fromType;
    trackGrid[toRow][toCol].content = cellImage(fromType);
    

    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?


  2. 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?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search