I have a list and I am using a for loop to loop through it. The structure looks like this:
salesLists: {
1: [ [], [], [] ]
2: [ [], [] ]
}
And html:
<div v-for="(saleLists, index) in salesLists">
<my-p v-for="(item, i) in saleLists" :key="i" :index="parseInt(i)+1"></my-p>
</div>
Now, I am trying to remove items from salesLists[1]
array. I have a button for that and @click="removeForm"
:
removeForm(e) {
var index = parseInt(e.target.getAttribute('data-index')) - 1 // = 2
var client = e.target.getAttribute('data-client') // = 1
//Vue.delete(this.salesLists[client], index);
this.salesLists[client].splice(index, 1)
this.$forceUpdate()
}
It removes it, however, as I didn't specify any keys and it's just empty arrays (i assume), it is not removing the right element from the DOM. It removes index of 2, but as it is v-for
looping through the item, and count reduces, it only removes the last item in the end.
What is the proper way of overe this issue? :/
Here is a Fiddle: / try writing different values for each input field and removing the middle one, you'll see it will remove the last one
I have a list and I am using a for loop to loop through it. The structure looks like this:
salesLists: {
1: [ [], [], [] ]
2: [ [], [] ]
}
And html:
<div v-for="(saleLists, index) in salesLists">
<my-p v-for="(item, i) in saleLists" :key="i" :index="parseInt(i)+1"></my-p>
</div>
Now, I am trying to remove items from salesLists[1]
array. I have a button for that and @click="removeForm"
:
removeForm(e) {
var index = parseInt(e.target.getAttribute('data-index')) - 1 // = 2
var client = e.target.getAttribute('data-client') // = 1
//Vue.delete(this.salesLists[client], index);
this.salesLists[client].splice(index, 1)
this.$forceUpdate()
}
It removes it, however, as I didn't specify any keys and it's just empty arrays (i assume), it is not removing the right element from the DOM. It removes index of 2, but as it is v-for
looping through the item, and count reduces, it only removes the last item in the end.
What is the proper way of overe this issue? :/
Here is a Fiddle: https://jsfiddle/8rvfz40n/ try writing different values for each input field and removing the middle one, you'll see it will remove the last one
Share Improve this question edited Aug 12, 2017 at 21:47 senty asked Aug 12, 2017 at 21:30 sentysenty 12.8k29 gold badges140 silver badges267 bronze badges 1- And why do you think it removes wrong item? I see that it works properly and removes correct one. jsfiddle/8rvfz40n/1 – dfsq Commented Aug 12, 2017 at 21:57
3 Answers
Reset to default 13<div v-for="(saleLists, index) in salesLists"> <my-p v-for="(item, i) in saleLists" :key="i" :index="parseInt(i)+1"></my-p> </div>
The use of the index as key is the problem, when you delete a item from the middle the index that is lost is the last.
In my case the solution that I found is add a unique "Hash" to the items, like an ID, but if the items are news, the ID is null.
The hash that I use is a timestamp:
Hash: new Date().getTime()
And then:
<div v-for="(saleLists, index) in salesLists">
<my-p v-for="(item, i) in saleLists" :key="item.Hash" :index="parseInt(i)+1"></my-p>
</div>
this one trips up a lot of people I think.
I've written an answer to this in the vue forum a while back at https://forum.vuejs/t/solved-array-of-ponents-wrong-after-element-remove/11866/3
so the problem is this
you have an array [rec1,rec2,rec3]
the keys for that array are 0, 1, 2
if you remove item at index of 1 you'd get an array with values [rec1, rec3], however the keys would be [0, 1], as the array does not skip the index after you remove it. Once you're in the template drawing it, since you don't have the key defined, the change the ponent sees is that the key or index 2 is missing, which is the last item, so it removes it.
to solve that, you need to find a different way to make sure you're targeting the intended item
https://jsfiddle/8rvfz40n/2/
in your case, using the item list
instead of the index i
will remove the intended item
<div id="app">
<div v-for="lists in xLists">
<my-p v-for="(list, i) in lists" :list="list"></my-p>
</div>
</div>
I should mention that another alternative is to store the unique key somehow inside the array, but as you you can imagine, that can be harder to maintain
Your issue is actually pretty straightforward: you lose the reference to the object key in xLists
in your first loop. If you store the object key and pass it on as a prop, you will retain that reference:
<div v-for="(saleLists, index) in salesLists">
<!-- index will refer to the object key -->
<my-p v-for="(item, i) in saleLists" :key="i" :index="i+1" :sales-list-index=":index"></my-p>
</div>
And you can simply retrieve the prop salesListIndex
and use it as the key to point to the correct nested array in your object. It is not clear how the ponent in your actual example is written, but with reference to your fiddle (I have used alphabetical keys so that you can tell if it's an object key or an array key, but implementation-wise it is identical):
Vue.ponent('my-p', {
props: ['index', 'value', 'listKey'],
template: `
<div>
<p>xListsKey: {{ listKey }}, index: {{ index }}</p>
<input :value="value" />
<button :data-index="index" @click="remove">del </button>
</div>
`,
methods: {
remove(e) {
var index = e.target.getAttribute('data-index');
this.$parent.xLists[this.listKey].splice(index, 1)
}
}
})
new Vue({
el: '#app',
data: {
xLists: {
'aa': [
['lorem'],
['ipsum'],
['dolor']
],
'bb': [
['foo'],
['bar']
]
}
}
})
<script src="https://unpkg./vue"></script>
<div id="app">
<div v-for="(lists, listKey) in xLists" :key="listKey">
<my-p v-for="(list, i) in lists" :key="i" :index="i" :value="list" :list-key="listKey"></my-p>
</div>
</div>