Skip to content

Commit 2cf0eec

Browse files
committed
feat: allow multiple messages in a validator fn closes #4322 #4318
1 parent 1c3ed01 commit 2cf0eec

File tree

5 files changed

+36
-5
lines changed

5 files changed

+36
-5
lines changed

.changeset/slimy-laws-drive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'vee-validate': patch
3+
---
4+
5+
feat: allow multiple messages in a validator fn closes #4322 #4318

packages/vee-validate/src/types/common.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export type GenericObject = Record<string, any>;
66

77
export type MaybeArray<T> = T | T[];
88

9+
export type MaybePromise<T> = T | Promise<T>;
10+
911
export type MapValuesPathsToRefs<
1012
TValues extends GenericObject,
1113
TPaths extends readonly [...MaybeRef<Path<TValues>>[]]

packages/vee-validate/src/types/forms.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ComputedRef, DeepReadonly, Ref, MaybeRef, MaybeRefOrGetter } from 'vue';
2-
import { MapValuesPathsToRefs, GenericObject } from './common';
2+
import { MapValuesPathsToRefs, GenericObject, MaybeArray, MaybePromise } from './common';
33
import { FieldValidationMetaInfo } from '../../../shared';
44
import { Path, PathValue } from './paths';
55
import { PartialDeep } from 'type-fest';
@@ -149,7 +149,7 @@ export type FieldContext<TValue = unknown> = Omit<PrivateFieldContext<TValue>, '
149149
export type GenericValidateFunction<TValue = unknown> = (
150150
value: TValue,
151151
ctx: FieldValidationMetaInfo
152-
) => boolean | string | Promise<boolean | string>;
152+
) => MaybePromise<boolean | MaybeArray<string>>;
153153

154154
export interface FormState<TValues> {
155155
values: PartialDeep<TValues>;

packages/vee-validate/src/validate.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,17 @@ async function _validate<TValue = unknown>(field: FieldValidationContext<TValue>
9393
for (let i = 0; i < length; i++) {
9494
const rule = pipeline[i];
9595
const result = await rule(value, ctx);
96-
const isValid = typeof result !== 'string' && result;
96+
const isValid = typeof result !== 'string' && !Array.isArray(result) && result;
9797
if (isValid) {
9898
continue;
9999
}
100100

101-
const message = typeof result === 'string' ? result : _generateFieldError(ctx);
102-
errors.push(message);
101+
if (Array.isArray(result)) {
102+
errors.push(...result);
103+
} else {
104+
const message = typeof result === 'string' ? result : _generateFieldError(ctx);
105+
errors.push(message);
106+
}
103107

104108
if (field.bails) {
105109
return {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,4 +893,24 @@ describe('useField()', () => {
893893
await flushPromises();
894894
expect(field.value.value).toBe(123);
895895
});
896+
897+
test('a validator can return multiple messages', async () => {
898+
let field!: FieldContext;
899+
const validator = vi.fn(val => (val ? true : [REQUIRED_MESSAGE, 'second']));
900+
901+
mountWithHoc({
902+
setup() {
903+
field = useField('field', validator);
904+
905+
return {};
906+
},
907+
});
908+
909+
await flushPromises();
910+
expect(field.errors.value).toHaveLength(0);
911+
await field.validate();
912+
await flushPromises();
913+
expect(field.errors.value).toHaveLength(2);
914+
expect(field.errors.value).toEqual([REQUIRED_MESSAGE, 'second']);
915+
});
896916
});

0 commit comments

Comments
 (0)