diff --git a/docs/api/use-form.md b/docs/api/use-form.md index f800a2f..bdc6257 100644 --- a/docs/api/use-form.md +++ b/docs/api/use-form.md @@ -93,7 +93,7 @@ This is initial touched status of the form. This option allows you to configure the validation strategy **before** first submit. -- Type `'blur' | 'input' | 'change' | 'submit'` +- Type `'blur' | 'input' | 'change' | 'submit' | ('blur' | 'input' | 'change' | 'submit')[]` - Default `'submit'` ### reValidateMode @@ -104,7 +104,7 @@ This option allows you to configure the validation strategy **after** first subm Even if your `reValidationMode` is not set to `submit`, Vorms will revalidate before submitting. ::: -- Type `'blur' | 'input' | 'change' | 'submit'` +- Type `'blur' | 'input' | 'change' | 'submit' | ('blur' | 'input' | 'change' | 'submit')[]` - Default `'change'` ### validateOnMounted diff --git a/packages/core/src/composables/useForm.ts b/packages/core/src/composables/useForm.ts index 529b746..cdd25ff 100644 --- a/packages/core/src/composables/useForm.ts +++ b/packages/core/src/composables/useForm.ts @@ -63,8 +63,8 @@ export interface UseFormOptions< initialValues: Values; initialErrors?: FormErrors; initialTouched?: FormTouched; - validateMode?: ValidateMode; - reValidateMode?: ValidateMode; + validateMode?: ValidateMode | ValidateMode[]; + reValidateMode?: ValidateMode | ValidateMode[]; validateOnMounted?: boolean; onSubmit: ( values: ValidatedValues extends FormValues ? ValidatedValues : Values, @@ -231,9 +231,6 @@ export function useForm< const fieldArrayRegistry: FieldArrayRegistry = {}; const dirty = computed(() => !isEqual(state.values, initialValues)); - const validateTiming = computed(() => - state.submitCount.value === 0 ? validateMode : reValidateMode, - ); const registerField = ( name: MaybeRefOrGetter, @@ -252,6 +249,14 @@ export function useForm< }; }; + const validateTimingInclude = (mode: ValidateMode) => { + const validateTiming = + state.submitCount.value === 0 ? validateMode : reValidateMode; + return isString(validateTiming) + ? mode === validateTiming + : validateTiming.includes(mode); + }; + const setFieldTouched = (name: string, touched = true) => { dispatch({ type: ACTION_TYPE.SET_TOUCHED, @@ -261,7 +266,7 @@ export function useForm< }, }); - return validateTiming.value === 'blur' + return validateTimingInclude('blur') ? runAllValidateHandler(state.values) : Promise.resolve(); }; @@ -273,9 +278,7 @@ export function useForm< }); const willValidate = - shouldValidate == null - ? validateTiming.value === 'change' - : shouldValidate; + shouldValidate == null ? validateTimingInclude('change') : shouldValidate; return willValidate ? runAllValidateHandler(state.values) @@ -382,13 +385,13 @@ export function useForm< }; const handleChange: FormEventHandler['handleChange'] = () => { - if (validateTiming.value === 'change') { + if (validateTimingInclude('change')) { runAllValidateHandler(state.values); } }; const handleInput: FormEventHandler['handleInput'] = () => { - if (validateTiming.value === 'input') { + if (validateTimingInclude('input')) { runAllValidateHandler(state.values); } }; diff --git a/packages/core/tests/composables/useForm.test.ts b/packages/core/tests/composables/useForm.test.ts index 8494213..2e00d13 100644 --- a/packages/core/tests/composables/useForm.test.ts +++ b/packages/core/tests/composables/useForm.test.ts @@ -293,6 +293,116 @@ describe('useForm', () => { expect(validate).toHaveBeenCalledTimes(1); }); + it('when validateMode is array [blur]', async () => { + const validate = vi.fn(); + const Comp = defineComponent({ + setup() { + const { register } = useForm({ + initialValues: { + name: '', + }, + validateMode: ['blur'], + validate, + onSubmit: noop, + }); + + const { value, attrs } = register('name'); + + return { value, attrs }; + }, + template: ` + + `, + }); + + const wrapper = mount(Comp); + await wrapper.find('input').trigger('blur'); + expect(validate).toHaveBeenCalledTimes(1); + }); + + it('when validateMode is array [change]', async () => { + const validate = vi.fn(); + const Comp = defineComponent({ + setup() { + const { register } = useForm({ + initialValues: { + name: '', + }, + validateMode: ['change'], + validate, + onSubmit: noop, + }); + + const { value, attrs } = register('name'); + + return { value, attrs }; + }, + template: ` + + `, + }); + + const wrapper = mount(Comp); + await wrapper.find('input').trigger('change'); + expect(validate).toHaveBeenCalledTimes(1); + }); + + it('when validateMode is array [input]', async () => { + const validate = vi.fn(); + const Comp = defineComponent({ + setup() { + const { register } = useForm({ + initialValues: { + name: '', + }, + validateMode: ['input'], + validate, + onSubmit: noop, + }); + + const { value, attrs } = register('name'); + + return { value, attrs }; + }, + template: ` + + `, + }); + + const wrapper = mount(Comp); + await wrapper.find('input').trigger('input'); + expect(validate).toHaveBeenCalledTimes(1); + }); + + it('when validateMode is array [blur, change, input]', async () => { + const validate = vi.fn(); + const Comp = defineComponent({ + setup() { + const { register } = useForm({ + initialValues: { + name: '', + }, + validateMode: ['blur', 'change', 'input'], + validate, + onSubmit: noop, + }); + + const { value, attrs } = register('name'); + + return { value, attrs }; + }, + template: ` + + `, + }); + + const wrapper = mount(Comp); + await wrapper.find('input').trigger('blur'); + await wrapper.find('input').trigger('change'); + await wrapper.find('input').trigger('input'); + expect(validate).toHaveBeenCalledTimes(3); + }); + it('when reValidateMode is default', async () => { const validate = vi.fn(); const Comp = defineComponent({ @@ -432,6 +542,44 @@ describe('useForm', () => { expect(validate).toHaveBeenCalledTimes(2); }); + it('when reValidateMode is array [blur, change, input]', async () => { + const validate = vi.fn(); + const Comp = defineComponent({ + setup() { + const { register, handleSubmit } = useForm({ + initialValues: { + name: '', + }, + validate, + reValidateMode: ['blur', 'change', 'input'], + onSubmit: noop, + }); + + const { value, attrs } = register('name'); + + return { value, attrs, handleSubmit }; + }, + template: ` +
+ + +
+ `, + }); + + document.body.innerHTML = `
`; + const wrapper = mount(Comp, { + attachTo: '#app', + }); + + await wrapper.find('button[type="submit"]').trigger('click'); + await wrapper.find('input').trigger('change'); + await wrapper.find('input').trigger('blur'); + await wrapper.find('input').trigger('input'); + await wrapper.find('button[type="submit"]').trigger('click'); + expect(validate).toHaveBeenCalledTimes(5); + }); + it('when validateOnMounted is true', async () => { const validate = vi.fn(); const Comp = defineComponent({