VueJS — рендеринг формы в цикле v-for, который ссылается на изначально пустой массив объектов

Постановка задачи: Я пытаюсь работать над дизайном, который включает в себя копирование 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 есть пустой элемент формы, то он удаляется.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
1 659
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В updatePartiesInfoList непосредственно перед отправкой нового объекта в partiesInfoList проверьте, возвращает ли this.partiesInfoList[0].fullName === 'dummy'true, затем, если условие истинно, выполните this.partiesInfoList.splice(0, 1), чтобы удалить фиктивный объект из вашего массива!

Ответ принят как подходящий

Ключи в вашем v-for должны быть идентификатором, а не индексом. Vue связывает каждый компонент со своим ключом.

С ключами он будет переупорядочивать элементы на основе изменения порядка ключей, а элементы с ключами, которых больше нет, всегда будут удаляться/уничтожаться.

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"
        >

Другие вопросы по теме