Skip to content

Commit fe51c12

Browse files
committed
fix: clean up the old values path when fields exchange names fixes #3325
1 parent 93ceeb7 commit fe51c12

2 files changed

Lines changed: 66 additions & 1 deletion

File tree

packages/vee-validate/src/useForm.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,15 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
269269
// necessary for fields generated by loops
270270
watch(
271271
field.name,
272-
newPath => {
272+
(newPath, oldPath) => {
273273
setFieldValue(newPath, valuesByFid[field.fid]);
274+
const isSharingName = fields.value.find(f => unref(f.name) === oldPath);
275+
// clean up the old path if no other field is sharing that name
276+
// #3325
277+
if (!isSharingName) {
278+
unsetPath(formValues, oldPath);
279+
unsetPath(initialValues.value, oldPath);
280+
}
274281
},
275282
{
276283
flush: 'post',
@@ -296,6 +303,7 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
296303
// so remove the field value key immediately
297304
if (field.idx === -1) {
298305
// avoid un-setting the value if the field was switched with another that shares the same name
306+
// they will be unset once the new field takes over the new name, look at `#registerField()`
299307
// #3166
300308
const isSharingName = fields.value.find(f => unref(f.name) === fieldName);
301309
if (isSharingName) {

packages/vee-validate/tests/Form.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,4 +1988,61 @@ describe('<Form />', () => {
19881988
expect(passwordError.textContent).toBe(errorMessage);
19891989
expect(passwordValue.textContent).toBe(value);
19901990
});
1991+
1992+
// #3325
1993+
test('unsets old path value when array fields are removed', async () => {
1994+
const onSubmit = jest.fn();
1995+
mountWithHoc({
1996+
setup() {
1997+
const users = ref([
1998+
{ id: 1, name: '111' },
1999+
{ id: 2, name: '222' },
2000+
{ id: 3, name: '333' },
2001+
]);
2002+
2003+
function remove(idx: number) {
2004+
users.value.splice(idx, 1);
2005+
}
2006+
2007+
return {
2008+
onSubmit,
2009+
users,
2010+
remove,
2011+
};
2012+
},
2013+
template: `
2014+
<VForm @submit="onSubmit">
2015+
<fieldset v-for="(user, idx) in users" :key="user.id">
2016+
<legend>User #{{ idx }}</legend>
2017+
<label :for="'name_' + idx">Name</label>
2018+
<Field :id="'name_' + idx" :name="'users[' + idx + '].name'" />
2019+
<ErrorMessage :name="'users[' + idx + '].name'" />
2020+
2021+
<button class="remove" type="button" @click="remove(idx)">X</button>
2022+
</fieldset>
2023+
2024+
<button class="submit" type="submit">Submit</button>
2025+
</VForm>
2026+
`,
2027+
});
2028+
2029+
await flushPromises();
2030+
const submitBtn = document.querySelector('.submit') as HTMLButtonElement;
2031+
const inputs = Array.from(document.querySelectorAll('input')) as HTMLInputElement[];
2032+
const removeBtn = document.querySelectorAll('.remove')[1] as HTMLButtonElement; // remove the second item
2033+
setValue(inputs[0], '111');
2034+
setValue(inputs[1], '222');
2035+
setValue(inputs[2], '333');
2036+
await flushPromises();
2037+
removeBtn.click();
2038+
await flushPromises();
2039+
(submitBtn as HTMLButtonElement).click();
2040+
await flushPromises();
2041+
expect(onSubmit).toHaveBeenCalledWith(
2042+
expect.objectContaining({
2043+
users: [{ name: '111' }, { name: '333' }],
2044+
}),
2045+
expect.anything()
2046+
);
2047+
});
19912048
});

0 commit comments

Comments
 (0)