Reputation: 36341
I am trying to move one item in an array, at position x
to position 2
. The below code works and moves the item to the new position. However, vue doesn't update the DOM
.
This is the code that I am using:
export default class LayersPanel extends Vue {
@ProvideReactive() public layers: any[] = [
{ name: 'Layer 1' }, { name: 'Layer 2' }, { name: 'Layer 3' }, { name: 'Layer 4' }
]
public onDragDrop(evt: DragEvent) {
let offset = parseInt(evt.dataTransfer.getData('text/plain'))
this.layers.splice(2, 0, this.layers.splice(offset, 1)[0])
}
}
<template>
<div class="layers-panel" @dragover="onDragOver" @drop="onDragDrop">
<layer v-for="(layer, index) in layers" :key="index" :info="layer" :offset="index"></layer>
</div>
</template>
I am not sure if this relates to this note in the docs or not:
When you modify an Array by directly setting an index (e.g. arr[0] = val) or modifying its length property. Similarly, Vue.js cannot pickup these changes. Always modify arrays by using an Array instance method, or replacing it entirely. Vue provides a convenience method arr.$set(index, value) which is syntax sugar for arr.splice(index, 1, value).
Supposedly .splice()
is a mutation method, so I don't think that it should matter. What am I doing wrong here?
Upvotes: 2
Views: 1091
Reputation: 29112
A likely source of problems is your choice of key
:
<layer v-for="(layer, index) in layers" :key="index" :info="layer" :offset="index"></layer>
Initially the <layer>
components will have keys 0
, 1
, 2
and 3
, corresponding to layers Layer 1
through to Layer 4
respectively.
If you then move the layer in position 0 to position 2 then you'll now have them in the order Layer 2
, Layer 3
, Layer 1
, Layer 4
. However, the keys are determined by index so Layer 2
will now have a key value of 0
.
When Vue tries to pair up the components after a re-render it uses the keys. So the <layer>
with a key
of 0
used to correspond to Layer 1
but it will now correspond to Layer 2
.
That isn't necessarily a problem. Layer 4
is easy: it hasn't moved, so it won't be changed. The other three have moved but they should have their info
props updated accordingly.
This is where the problem of stateful components comes in. While the first three components will have their info
props updated that won't necessarily have any impact on any other internal state, such as that held by data
properties. Those will still retain the value they had for the original info
value.
The solution here would generally be to use a more appropriate value for the key
. It is unclear from the question what such a value might be in your case. In some cases you may need to add an extra property within each array item just to give it a suitable property.
The important thing is that the keys are distinct and tied to the array items. If the layer's name
is a unique identifier then you could use that, e.g. :key="layer.name"
.
Upvotes: 1