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