Skip to content

Commit

Permalink
fix: clean up the old values path when fields exchange names fixes #3325
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed May 28, 2021
1 parent 93ceeb7 commit fe51c12
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 1 deletion.
10 changes: 9 additions & 1 deletion packages/vee-validate/src/useForm.ts
Expand Up @@ -269,8 +269,15 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
// necessary for fields generated by loops
watch(
field.name,
newPath => {
(newPath, oldPath) => {
setFieldValue(newPath, valuesByFid[field.fid]);
const isSharingName = fields.value.find(f => unref(f.name) === oldPath);
// clean up the old path if no other field is sharing that name
// #3325
if (!isSharingName) {
unsetPath(formValues, oldPath);
unsetPath(initialValues.value, oldPath);
}
},
{
flush: 'post',
Expand All @@ -296,6 +303,7 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
// so remove the field value key immediately
if (field.idx === -1) {
// avoid un-setting the value if the field was switched with another that shares the same name
// they will be unset once the new field takes over the new name, look at `#registerField()`
// #3166
const isSharingName = fields.value.find(f => unref(f.name) === fieldName);
if (isSharingName) {
Expand Down
57 changes: 57 additions & 0 deletions packages/vee-validate/tests/Form.spec.ts
Expand Up @@ -1988,4 +1988,61 @@ describe('<Form />', () => {
expect(passwordError.textContent).toBe(errorMessage);
expect(passwordValue.textContent).toBe(value);
});

// #3325
test('unsets old path value when array fields are removed', async () => {
const onSubmit = jest.fn();
mountWithHoc({
setup() {
const users = ref([
{ id: 1, name: '111' },
{ id: 2, name: '222' },
{ id: 3, name: '333' },
]);

function remove(idx: number) {
users.value.splice(idx, 1);
}

return {
onSubmit,
users,
remove,
};
},
template: `
<VForm @submit="onSubmit">
<fieldset v-for="(user, idx) in users" :key="user.id">
<legend>User #{{ idx }}</legend>
<label :for="'name_' + idx">Name</label>
<Field :id="'name_' + idx" :name="'users[' + idx + '].name'" />
<ErrorMessage :name="'users[' + idx + '].name'" />
<button class="remove" type="button" @click="remove(idx)">X</button>
</fieldset>
<button class="submit" type="submit">Submit</button>
</VForm>
`,
});

await flushPromises();
const submitBtn = document.querySelector('.submit') as HTMLButtonElement;
const inputs = Array.from(document.querySelectorAll('input')) as HTMLInputElement[];
const removeBtn = document.querySelectorAll('.remove')[1] as HTMLButtonElement; // remove the second item
setValue(inputs[0], '111');
setValue(inputs[1], '222');
setValue(inputs[2], '333');
await flushPromises();
removeBtn.click();
await flushPromises();
(submitBtn as HTMLButtonElement).click();
await flushPromises();
expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({
users: [{ name: '111' }, { name: '333' }],
}),
expect.anything()
);
});
});

0 comments on commit fe51c12

Please sign in to comment.