Постановка задачи:
Я пытаюсь работать над дизайном, который включает в себя копирование Parties Being Served
полей формы при каждом Add button
клике. При нажатии на Add Button
соответствующий PartyInfo
объект добавляется в массив PartyInfoList
В файле BaseButtonList.vue я использую массив PartyInfoList
для настройки цикла v-for.
<base-card
v-for = "(data, index) of partiesInfoList" :key = "index"
ref = "childComponent"
@add-parties = "updatePartiesInfoList"
@delete-party = "deleteParty(index)"
:lastElement = "index === partiesInfoListLength - 1"
>
Однако важно отметить, что изначально массив не имеет элементов. Но, поскольку у меня нет другого способа ввести данные в массив, я вынужден загрузить форму, несмотря на то, что в массиве partiesInfoList
нет элементов. Я достигаю этого, устанавливая partiesInfoList
с фиктивными данными. Это позволяет отображать начальную форму. И когда нажимается кнопка Add Another
, эта первая реальная запись будет перемещена во 2-й индекс partiesInfoList
.
Однако это вызывает серьезные проблемы при удалении записей из массива. splice
работает правильно, когда я консольно записываю вывод. Но в конечном итоге Vue удаляет не тот элемент из DOM.
Я использовал уникальный ключ index
, а также пробовал другие возможные ключи, но все они приводят к одной и той же пагубной ошибке. Любая помощь будет принята с благодарностью. Я думаю, что это действительно сложный шаблон проектирования, я могу придумать более простые альтернативы, но я хочу, чтобы это работало как вызов. Может быть, есть лучший поток данных, который я могу настроить или что-то в этом роде.
Прилагается код
BaseCard.vue
<template>
//contains the template for the input form element
</template>
<script>
import { random } from '@amcharts/amcharts4/.internal/core/utils/String';
import { EventBus } from './bus.js';
export default {
emits:['add-parties','delete-party'],
props:['lastElement'],
data() {
return {
partyInfo:
{
id: '',
fullName: '',
preAuthorize: '',
serviceAddress: '',
},
validation: {
fullNameIsValid: true,
serviceAddressIsValid: true
},
hideAddButton: false,
formIsValid: true,
addServiceButtonText: '+ Add Service Notes (Optional)',
serviceNotes: [],
showServiceNotes: false,
showDeleteButton: true,
enteredServiceNote: '', //service notes addendum
}
},
computed : {
showServiceNotex(){
if (!this.showServiceNotes){
return '+Add Service Notes (Optional)'
}else{
return '- Remove Service Notes';
}
}
},
methods: {
setServiceNotes(){
this.showServiceNotes = !this.showServiceNotes;
},
addAnotherParty(){
this.validateForm();
if (this.counter === 0){
this.counter++;
this.lastElement = false;
}
if (!this.formIsValid){
return;
}
let emitObj = JSON.parse(JSON.stringify(this.partyInfo));
this.$emit('add-parties', emitObj); //event
},
deleteParty(){
this.$emit('delete-party');
},
validateForm(){
this.formIsValid = true;
if (this.partyInfo.fullName === ''){
this.validation.fullNameIsValid = false;
this.formIsValid = false;
}
if (this.partyInfo.serviceAddress === ''){
this.validation.serviceAddressIsValid = false;
this.formIsValid = false;
}
},
clearValidity(input){
this.validation[input] = true;
},
clearForm(){
this.partyInfo.fullName = '';
this.partyInfo.serviceAddress = '';
this.partyInfo.preAuthorize = false;
}
},
created(){
console.info('created');
}
}
</script>
Прилагается <BaseCardList>
, который отображает элементы формы в цикле v-for
.
BaseCardList.vue
<template>
<ul>
<base-card
v-for = "(data, index) of partiesInfoList" :key = "index"
ref = "childComponent"
@add-parties = "updatePartiesInfoList"
@delete-party = "deleteParty(index)"
:lastElement = "index === partiesInfoListLength - 1"
>
<!-- Wrapper for the `Parties Being Served` component-->
<template v-slot:title>
<slot></slot>
</template>
</base-card>
</ul>
</template>
<script>
import BaseCard from './BaseCard.vue';
export default {
components: { BaseCard },
data() {
return {
selectedComponent: 'base-card',
partiesInfoList : [
{id: 0,
fullName: 'dummy',
serviceAddress: 'dummy',
preAuthorize: ''
}
],
clearForm: false,
counter: 1
}
},
computed : {
hasParty(){
return this.partiesInfoList.length > 0;
},
partiesInfoListLength(){
return this.partiesInfoList.length;
}
},
methods: {
updatePartiesInfoList(additionalInfo){
// if (this.counter == 0){
// this.partiesInfoList.splice(0,1);
// }
this.partiesInfoList.push(additionalInfo);
this.counter++;
console.info(this.partiesInfoList);
console.info('The length of list is '+this.partiesInfoList.length);
},
deleteParty(resId){
// const resIndex = this.partiesInfoList.findIndex(
// res => res.id === resId
// );
// this.partiesInfoList.splice(resIndex, 1);
if (this.counter == 1){
return;
}
this.partiesInfoList.splice(resId, 1);
console.info('Index is '+resId);
console.info('after del');
console.info(this.partiesInfoList);
}
}
}
</script>
Фактическая ошибка вывода на экран: соседний элемент удаляется из DOM. Скажем, я нажимаю «Удалить» для «Второго», но «Третий» удаляется. Но если в конце v-for есть пустой элемент формы, то он удаляется.
В updatePartiesInfoList
непосредственно перед отправкой нового объекта в partiesInfoList
проверьте, возвращает ли this.partiesInfoList[0].fullName === 'dummy'
true
, затем, если условие истинно, выполните this.partiesInfoList.splice(0, 1)
, чтобы удалить фиктивный объект из вашего массива!
Ключи в вашем v-for
должны быть идентификатором, а не индексом. Vue связывает каждый компонент со своим ключом.
С ключами он будет переупорядочивать элементы на основе изменения порядка ключей, а элементы с ключами, которых больше нет, всегда будут удаляться/уничтожаться.
Когда вы соединяете элемент partyInfoList
, Vue перерегистрирует индексы. Индексы каскадируются, и, поскольку теперь в partyInfoList
есть 2 элемента вместо 3, компонент Vue с индексом 2
или последний компонент удаляется.
// partyInfoList
[
// index: 0
{
id: 0,
fullName: 'first',
serviceAddress: '1',
preAuthorize: ''
},
// index: 1
{
id: 1,
fullName: 'second',
serviceAddress: '2',
preAuthorize: ''
},
// index: 2
{
id: 2,
fullName: 'third',
serviceAddress: '3',
preAuthorize: ''
},
]
Сращивание
// resId = 1
this.partiesInfoList.splice(resId, 1);
Результирующий массив
// partyInfoList
[
// index: 0
{
id: 0,
fullName: 'first',
serviceAddress: '1',
preAuthorize: ''
},
// index: 1
{
id: 2,
fullName: 'third',
serviceAddress: '3',
preAuthorize: ''
},
]
Массив выглядит нормально, а вот шаблоны на экране нет. Это связано с тем, что Vue увидел, что элемент partyInfoList
с индексом 2
больше не существует, поэтому компонент с :key = "2"
и все его данные были удалены из DOM.
Ваше решение на самом деле очень простое. Все, что вам нужно сделать, это изменить :key = "index"
на :key = "data.id"
. Это свяжет ваш компонент с данными, а не с динамическим значением, таким как индекс.
Я также не знаю, как вы устанавливаете id
для каждого элемента partyInfoList
, но они должны быть уникальными, чтобы ключи работали.
Вот как должен выглядеть ваш новый код:
<base-card
v-for = "(data, index) of partiesInfoList" :key = "data.id"
ref = "childComponent"
@add-parties = "updatePartiesInfoList"
@delete-party = "deleteParty(index)"
:lastElement = "index === partiesInfoListLength - 1"
>