render v-for vue.js
я вывожу список с помощью v-for, затем когда я удаляю элемент массива по индексу методом splice он удаляется как и ожидалось, но v-for продолжает отображать несуществующей элемент, при этом перестаёт отображать последний элемент массива.
ПОЧЕМУ ТАК ПРОИСХОДИТ ? И КАК ЭТО ИСПРАВИТЬ ?
Родительский компонент:
<template>
<div id="app" class="w_max h_max grid grid_center grid_vertical_center">
<ul class="main">
<item v-for="(item, index) in tasks" :options="item" :key="index" @delete:item="dell(index)"></item>
</ul>
</div>
</template>
<script>
import item from '@/components/item'
export default {
name: 'App',
components: {
item
},
data () {
return {
list: [
{
text: 'text - 1'
},
{
text: 'text - 2'
},
{
text: 'text - 3'
}
]
}
},
methods: {
dell (index) {
this.list.splice(index, 1)
}
},
computed: {
tasks () {
return this.list
}
}
}
</script>
<style scoped>
#app {
padding: 40px 15px;
background-color: rgb(250, 242, 242);
}
.main {
max-width: 940px;
width: 100%;
min-height: 80%;
}
</style>
Дочерний компонент:
<template>
<li class="item grid grid_cenrical_center">
<div class="item__text">
<h2>{{ item.text }}</h2>
</div>
<div class="item__bottom">
<button @click="dell">dell</button>
</div>
</li>
</template>
<script>
export default {
props: [
'options'
],
data () {
return {
item: this.options
}
},
methods: {
dell () {
this.$emit('delete:item', true)
}
}
}
</script>
<style scoped>
.item {
padding: 10px 20px;
border-radius: 3px;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
background-color: white;
grid-template-columns: 5fr 1fr;
grid-column-gap: 15px;
margin-bottom: 1px;
}
.item__text {
font-weight: 100;
font-family: 'Times New Roman', Times, serif
}
</style>
Ответы (3 шт):
Ошибка в данном коде в том, что вы передали в дочерний компонент данные, а затем попытались их перенести в data, а это чревато тем, что пока ваш компонент не пройдет этап пересоздания, вы будете видеть неактуальные данные (хотя на самом деле this.list и task правильно формируется под действием splice()).
Объяснение почему так: на этапе создания дочерних компонентов, вы подписываете каждый компонент порядковым номером (исходя из очередности в массиве) - индексируете их порядковым номером (index) и когда вы удаляете элемент массива, дочерние компоненты не перерисовываются, а пытаются угодить вашей логике, что приводит к тому, что раз элементов стало на один меньше в массиве, то последнему компоненту нужно уйти (т.к. его индекса уже нет в массиве) и он успешно пропадает с поля зрения.
Таким образом у вас как минимум два варианта лечения данной проблемы, либо поставить условие для перерисовки дочерних компонентов после действий с данными, как вариант (обратите внимание на v-if в теге ul):
<template>
<ul v-if="item">
<li class="item grid grid_cenrical_center">
<div class="item__text">
<h2>{{ options.text }}</h2>
</div>
<div class="item__bottom">
<button @click="dell">dell</button>
</div>
</li>
</ul>
</template>
<script>
export default {
props: ["options"],
data() {
return {
item: this.options
};
},
methods: {
dell() {
this.$emit("delete:item", true);
}
}
};
</script>
<style scoped>
.item {
padding: 10px 20px;
border-radius: 3px;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
background-color: white;
grid-template-columns: 5fr 1fr;
grid-column-gap: 15px;
margin-bottom: 1px;
}
.item__text {
font-weight: 100;
font-family: "Times New Roman", Times, serif;
}
</style>
либо не использовать данные из props в data как в этом коде:
<template>
<ul>
<li class="item grid grid_cenrical_center">
<div class="item__text">
<h2>{{ options.text }}</h2>
</div>
<div class="item__bottom">
<button @click="dell">dell</button>
</div>
</li>
</ul>
</template>
<script>
export default {
props: ["options"],
data() {
return {
// item: this.options
};
},
methods: {
dell() {
this.$emit("delete:item", true);
}
}
};
</script>
<style scoped>
.item {
padding: 10px 20px;
border-radius: 3px;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
background-color: white;
grid-template-columns: 5fr 1fr;
grid-column-gap: 15px;
margin-bottom: 1px;
}
.item__text {
font-weight: 100;
font-family: "Times New Roman", Times, serif;
}
</style>
Я привел два варианта оформления дочернего компонента. При подготовке ответа, была использована информация с официальной документации Vue - Входные параметры (props) во Vue
Смотрите тут https://vuejs.org/v2/guide/list.html#Maintaining-State
Вы выбираете для key index, но индекс у массива не изменяется, а изменяется содержимое.
Лучше добавьте к элементам значения id и используйте их в качестве ключа.
Если посмотреть чуть дальше, то https://vuejs.org/v2/guide/list.html#Mutation-Methods
Уберите key чтоб не мешать Vue отслеживать содержимое массива.
Не используйте пожалуйста свойство :key вместе с index если собираешься модифицировать массив.
Каждый раз при удалении ты меняешь размер массива и твой список перерисовывается но непредсказуемым для тебя способом.
Свойство :key используется для понимания vue что требуется перерисовать в dom.
И получается следующее, ты удаляешь элемент из массива, размер массива меняется, ключи в виде индексов генерируются с 0 и ты видишь такое поведение.
В документации vue есть об этом упоминание. Ключи должны всегда быть уникальными. Считается хорошей практикой при работе со списками объявлять в объектах id и присваивать его key.
