diff --git a/packages/vee-validate/src/useForm.ts b/packages/vee-validate/src/useForm.ts index 122fc6181..10224603c 100644 --- a/packages/vee-validate/src/useForm.ts +++ b/packages/vee-validate/src/useForm.ts @@ -292,6 +292,9 @@ export function useForm = Record = Record { delete valuesByFid[fid]; + // clears a field error on unmounted + // we wait till next tick to make sure if the field is completely removed and doesn't have any siblings like checkboxes + // #3384 + if (!fieldsById.value[fieldName]) { + setFieldError(fieldName, undefined); + } }); const fieldName = unref(field.name); // in this case, this is a single field not a group (checkbox or radio) // 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()` @@ -353,6 +363,7 @@ export function useForm = Record', () => { // field was re-checked expect(span.textContent).toBe(''); }); + + test('field errors should be removed when its unmounted', async () => { + const isShown = ref(true); + const wrapper = mountWithHoc({ + setup() { + return { + isShown, + }; + }, + template: ` + + + {{ errors.fname }} + + `, + }); + + await flushPromises(); + const span = wrapper.$el.querySelector('span'); + setValue(wrapper.$el.querySelector('input'), ''); + await flushPromises(); + expect(span.textContent).toBe(REQUIRED_MESSAGE); + isShown.value = false; + + await flushPromises(); + // field was re-checked + expect(span.textContent).toBe(''); + }); });