Home > Mobile >  Why do my dynamically generated forms not update correctly when I delete one of them using Vue?
Why do my dynamically generated forms not update correctly when I delete one of them using Vue?

Time:06-24

I want to populate a form with multiple text input fields. However, when I delete one of these input fields, the UI doesn't update correctly according to the data present.

Condition.vue

        <template v-slot:custom-form>
            <el-row v-for="(condition, index) of customFormData" type="flex" :key="`${randomSalt}${index}`"  :justify="isElse(condition) ? 'start' : 'space-around'">
                <el-col  : :span=".5">
                {{condition.type.toLowerCase() === 'else' ? 'ELSE' : index > 0 ? 'ELSE IF' : 'IF'}}
                </el-col>
                <el-col ref="index" v-if="condition.type.toLowerCase() !== 'else'" :span="6">
                    <editable-value name="inputAction" dataType="property" v-model="condition.inputAction" :schema="condition.schema"></editable-value>
                </el-col>
                <el-col v-if="condition.type.toLowerCase() !== 'else'"  :span=".5">
                    =
                </el-col>
                <el-col v-if="condition.type.toLowerCase() !== 'else'" :span="6">
                    <editable-value name="inputActionValue" dataType="value" v-model="condition.inputActionValue" :schema="condition.schema"></editable-value>
                </el-col>
                <el-col v-if="condition.type.toLowerCase() !== 'else'"  :span=".5">
                    THEN
                </el-col>
                <el-col :span="6">
                    <editable-value name="ouputAction" dataType="output" v-model="condition.ouputAction" :schema="condition.schema"></editable-value>
                </el-col>
                <el-col :span=".5">
                    <el-button icon="el-icon-delete" type="text" plain  @click="e => { deleteCondition(index) }"></el-button>
                </el-col>
            </el-row>
        </template>
export default {
    components: { EditableValue },
    data () {
        return {
            customFormData: [
                {
                    type: 'if',
                    schema: Schema
                }
            ],
            salt: 1043423
        }
    },
    computed: {
        randomSalt () {
            return this.salt   this.getRnd()
        }
    },
    methods: {
        deleteCondition (index) {
            this.customFormData.splice(index, 1)
        },
        getRnd () {
            return Math.floor((Math.random() * 1000000 / (Math.random() - 1))   1)
        },
        chainCondition (type) {
            this.customFormData.push({ ...this.customFormData[this.customFormData.length], type, schema: this.schema })
        }
    }
}

Basically, each Editable-Values component represents an input field, customFormData is an array listing each row of input fields belonging to the main form. When deleting a row, we simply splice that row from the array and vice versa for addition.

Main Problem

Everything else works fine except when I delete a row that is neither the first nor last in the array. When such a deletion happens, the data (code) in the app is correct and represents the deletion.

The UI however, seems as if the row below the intended one was deleted. Using pseudocode, the behavior is as follows:

UI
<row 1 data="one"/>
<row 2 data="two" />
<row 3 data="three"/>

Code
data: {
 1: 'one',
 2: 'two',
 3: 'three'
}

// If we delete row 2, the UI becomes as follows:
UI
<row 1 data="one"/>
<row 2 data="two" />

Code
data: {
 1: 'one',
 3: 'three'
}

What can I do to make sure that each row represents its data correctly.?

I tried forcing a rerender by changing the row key, however, this just ends up displaying empty input fields. I have also tried using an object instead of an array to display the rows, but the results are much worse, data doesn't seem so reactive with objects.

What can I do to fix this?

CodePudding user response:

Your problem most probably comes from using v-for index as a key. key must be unique and stable - index satisfies only 1st condition (when the 2nd row is deleted, 3rd row now gets index 2 instead of 3 and Vue rendering mechanism makes false assumptions about what needs to be re-rendered/updated in the DOM)

Workaround with random key doesn't work either. computed caches the value and changes only when some of the reactive dependencies are changed (this.salt in this case) so you are actually using same key for all the rows. Changing it into method will lead to actually re-rendering everything all the time - this is not something you want either

Only way to solve it correctly is to assign unique ID to each row - it may very well be an index but it must be stored with each row so it does not change every time some row is deleted...

  • Related