【问题标题】:VueJS- Rendering form in a v-for loop that references an initially empty array of objectsVueJS- 在 v-for 循环中渲染表单,该循环引用最初为空的对象数组
【发布时间】:2021-04-03 15:18:00
【问题描述】:

问题陈述: 我正在尝试设计一个涉及在每个Add button 点击上复制Parties Being Served 表单字段的设计。单击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 按钮被点击时,第一个真正的条目将被推送到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.log('created');
       
    }
}
</script>

附加的是&lt;BaseCardList&gt;,它在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.log(this.partiesInfoList);
            console.log('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.log('Index is '+resId);
            console.log('after del');
            console.log(this.partiesInfoList);
        }
    }
}
</script>

屏幕上的实际输出错误:相邻元素从 DOM 中删除。说,我单击“第二”的删除,但“第三”被删除。但是,如果在 v-for 的末尾有一个空的表单元素,那么这个元素就会被删除。

【问题讨论】:

    标签: javascript arrays vue.js dom v-for


    【解决方案1】:

    v-for 中的键应该是 id,而不是索引。 Vue 将每个组件与其键相关联。

    使用键,它会根据键的顺序变化对元素重新排序,并且键不再存在的元素将始终被删除/销毁。

    vue documentation

    当你拼接partyInfoList 项时,Vue 会重新注册索引。索引向下级联,由于 partyInfoList 现在有 2 个项目而不是 3 个,因此索引为 2 或最后一个组件的 Vue 组件被删除。

    这是一个例子:

    // 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 发现索引为 2partyInfoList 项已不存在,因此将带有 :key="2" 的组件及其所有数据从 DOM 中删除。

    您的解决方案实际上非常简单。您需要做的就是将:key="index" 更改为:key="data.id"。这会将您的组件与数据联系起来,而不是像索引这样的动态值。

    我也不知道您是如何在每个 partyInfoList 项目上设置 id 的,但这些必须是唯一的才能使键起作用。

    你的新代码应该是这样的:

    <base-card
            v-for="(data, index) of partiesInfoList" :key="data.id"
            ref="childComponent"
            @add-parties="updatePartiesInfoList"
            @delete-party="deleteParty(index)"
            :lastElement="index ===  partiesInfoListLength - 1"
            >
    

    【讨论】:

      【解决方案2】:

      updatePartiesInfoList 中,在将新对象推送到partiesInfoList 之前,检查this.partiesInfoList[0].fullName === 'dummy' 是否返回true,如果条件为真,则执行this.partiesInfoList.splice(0, 1) 以从数组中删除虚拟对象!

      【讨论】:

        猜你喜欢
        • 2019-05-09
        • 1970-01-01
        • 2021-05-15
        • 2021-12-15
        • 2020-04-01
        • 1970-01-01
        • 2019-10-07
        • 2020-08-23
        • 1970-01-01
        相关资源
        最近更新 更多