diff --git a/.changeset/fix-4981-empty-array-error.md b/.changeset/fix-4981-empty-array-error.md new file mode 100644 index 000000000..d4aa5f2d7 --- /dev/null +++ b/.changeset/fix-4981-empty-array-error.md @@ -0,0 +1,5 @@ +--- +"vee-validate": patch +--- + +Fix error message not showing when field array is empty (#4981) diff --git a/packages/vee-validate/src/useForm.ts b/packages/vee-validate/src/useForm.ts index f5e7329b8..b7131e7b3 100644 --- a/packages/vee-validate/src/useForm.ts +++ b/packages/vee-validate/src/useForm.ts @@ -366,7 +366,9 @@ export function useForm< const results = paths.reduce( (validation, _path) => { const expectedPath = _path as Path; - const pathState = findPathState(expectedPath) || findHoistedPath(expectedPath); + const isFieldArrayPath = fieldArrays.some(a => toValue(a.path) === expectedPath); + const pathState = + findPathState(expectedPath) || (isFieldArrayPath ? undefined : findHoistedPath(expectedPath)); const messages = formResult.results[expectedPath]?.errors || []; // This is the real path of the field, because it might've been a hoisted field const path = (toValue(pathState?.path) || expectedPath) as Path; diff --git a/packages/vee-validate/tests/useFieldArray.spec.ts b/packages/vee-validate/tests/useFieldArray.spec.ts index 73bd05b8a..7acc8471a 100644 --- a/packages/vee-validate/tests/useFieldArray.spec.ts +++ b/packages/vee-validate/tests/useFieldArray.spec.ts @@ -521,6 +521,72 @@ test('array move initializes the array if undefined', async () => { expect(arr.fields.value).toHaveLength(0); }); +// #4981 +test('shows validation error when field array is empty after removing all items', async () => { + let form!: FormContext; + let arr!: FieldArrayContext; + mountWithHoc({ + setup() { + form = useForm({ + initialValues: { + users: ['one'], + }, + validationSchema: z.object({ + users: z.array(z.string()).min(1, 'At least one item is required'), + }), + }); + + arr = useFieldArray('users'); + }, + template: ` +
+ `, + }); + + await flushPromises(); + expect(form.meta.value.valid).toBe(true); + arr.remove(0); + await flushPromises(); + expect(form.meta.value.valid).toBe(false); + expect(form.errors.value['users']).toBe('At least one item is required'); +}); + +// #4981 +test('shows validation error at correct path when nested field array is empty', async () => { + let form!: FormContext; + let arr!: FieldArrayContext; + mountWithHoc({ + setup() { + form = useForm({ + initialValues: { + settings: { + items: ['one'], + }, + }, + validationSchema: z.object({ + settings: z.object({ + items: z.array(z.string()).min(1, 'At least one item is required'), + }), + }), + }); + + arr = useFieldArray('settings.items'); + }, + template: ` +
+ `, + }); + + await flushPromises(); + expect(form.meta.value.valid).toBe(true); + arr.remove(0); + await flushPromises(); + expect(form.meta.value.valid).toBe(false); + // The error should be at 'settings.items', NOT at 'settings' + expect(form.errors.value['settings.items']).toBe('At least one item is required'); + expect(form.errors.value['settings']).toBeUndefined(); +}); + // #4557 test('errors are available to the newly inserted items', async () => { let arr!: FieldArrayContext;