0

I have a simple UI where the user can define 1:n mappings for form fields. The model is very simple:

export interface IMapping {
    pdfName: string;
    fieldNames: string[];
}

The problem is, changes to fieldNames is not reactive. Here is my code for the form which should work for editing and adding a new mapping.

Template:

<form @submit.prevent>
    <div class="field">
        <label for="pdfName">PDF Field Name</label>
        <input type="text" name="pdfName" id="pdfName" required
            v-model="data.pdfName"/>
    </div>
    <div>
        <fieldset v-for="(fieldName, index) in data.fieldNames" :key="index">
            <legend>Form Field</legend>
            <div class="field">
                <label :for="'name_' + index">Name</label>
                <input type="text" name="name" :id="'name_' + index" required
                    v-model="fieldName"/>
            </div>
            <button @click="removeField(index)" class="removeField"><i class="fas fa-minus"></i></button>
        </fieldset>
        <button @click="addField"><i class="fas fa-plus"></i></button>
    </div>
    <div class="buttonrow">
        <button @click="save">Save</button>
        <button @click="cancel">Cancel</button>
    </div>
</form>

TypeScript component

import { Component, Emit, Prop, Vue, Watch } from 'vue-property-decorator';

import { IMapping } from '@/models/IMapping';

@Component
export default class MappingForm extends Vue {

    public data: IMapping | null = null;

    @Prop({type: Object})
    private mapping!: IMapping;

    public created() {
        this.data = {...this.mapping};
    }

    public addField() {
        this.data!.fieldNames.push('');
    }

    public removeField(index: number) {
        this.data!.fieldNames.splice(index, 1);
    }

    @Emit('mapping-changed')
    public save() {
        if (this.mapping === null) {
            return this.data;
        } else {
            Object.assign(this.mapping, this.data);
            return this.mapping;
        }
    }

    @Emit('mapping-unchanged')
    public cancel() {}

}

I copy the prop to a local variable so I can easily undo any changes and only apply them with save. It reacts to the push and the splice but the string values are not updated. I tried to use @Watch but I am not sure on how to use it in this case as I don't have a method to listen to, but only the user input on my <input type="text" name="name" :id="'name_' + index" required v-model="fieldName"/>

1 Answer 1

1

Try this:

<input type="text" name="name" :id="'name_' + index" required v-model="data.fieldNames[index]"/>

Maybe the problem is that v-for is not passing the reference because it's a primitive type.

Sign up to request clarification or add additional context in comments.

1 Comment

Haven't thought of that. Was thinking it might be the primitive type that causes the issue and considered having a dummy object with just a value prop instead. But this seems to work as expected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.