diff --git a/packages/core/src/composables/useForm.ts b/packages/core/src/composables/useForm.ts index 529b746..d59065f 100644 --- a/packages/core/src/composables/useForm.ts +++ b/packages/core/src/composables/useForm.ts @@ -1,7 +1,7 @@ import deepmerge from 'deepmerge'; import isEqual from 'fast-deep-equal/es6'; import { klona as deepClone } from 'klona/full'; -import { computed, onMounted, provide, reactive, ref } from 'vue'; +import { computed, onMounted, provide, reactive, ref, unref } from 'vue'; import toValue from './toValue'; import { FormContextKey } from './useFormContext'; @@ -49,6 +49,10 @@ interface FieldArrayRegistry { }; } +interface ValidateHandlerOptions { + onlyBlurred?: boolean; +} + export interface FormSubmitHelper { setSubmitting: (isSubmitting: boolean) => void; readonly initialValues: Values; @@ -262,7 +266,7 @@ export function useForm< }); return validateTiming.value === 'blur' - ? runAllValidateHandler(state.values) + ? runAllValidateHandler(state.values, { onlyBlurred: true }) : Promise.resolve(); }; @@ -503,20 +507,56 @@ export function useForm< }); }; - const runAllValidateHandler = (values: Values = state.values) => { + /** + * Creates a new object of errors, but only with the errors of touched fields. + * + * @param errors The union of field and form errors + * @returns The field errors that have been touched + */ + const getTouchedErrors = < + T extends FormErrors, + F extends FormTouched, + >( + errors: T, + touched: F, + ): Partial => { + const result: any = {}; + + for (const key in errors) { + if (typeof touched[key] === 'object' && typeof errors[key] === 'object') { + result[key] = getTouchedErrors( + errors[key] as FormErrors, + touched[key] as FormTouched, + ); + } else if (touched[key] === true && errors[key]) { + result[key] = errors[key]; + } + } + + return result; + }; + + const runAllValidateHandler = ( + values: Values = state.values, + validateOptions?: ValidateHandlerOptions, + ) => { dispatch({ type: ACTION_TYPE.SET_ISVALIDATING, payload: true }); return Promise.all([ runFieldValidateHandler(values), options.validate ? runValidateHandler(values) : {}, ]) .then(([fieldErrors, validateErrors]) => { - const errors = deepmerge.all>( + const baseErrors = deepmerge.all>( [fieldErrors, validateErrors], { arrayMerge, }, ); + const errors = validateOptions?.onlyBlurred + ? getTouchedErrors(baseErrors, unref(state.touched)) + : baseErrors; + setErrors(errors); return errors;