My UI is designed for users to create 1:n mappings for form fields. The structure of the model is quite straightforward:
export interface IMapping {
pdfName: string;
fieldNames: string[];
}
However, I encountered a problem with reactivity when it comes to changes made to the fieldNames
. Below is the code snippet for the form that allows users to edit or add 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 store the prop in a local variable to easily revert any changes and only apply them when saved. While it reacts to push and splice operations, the string values are not updated.
I attempted using @Watch
, but unsure how to implement it in this situation as there's no method to listen to other than user input on my
<input type="text" name="name" :id="'name_' + index" required v-model="fieldName"/>