Skip to content

Commit

Permalink
fix: change errors source to form closes #3177
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Feb 17, 2021
1 parent fb3d299 commit 7c13c92
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 44 deletions.
16 changes: 12 additions & 4 deletions packages/vee-validate/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,16 @@ export interface FormState<TValues> {
submitCount: number;
}

export type FormErrors<TValues extends Record<string, unknown>> = Partial<Record<keyof TValues, string | undefined>>;
export type FormErrorBag<TValues extends Record<string, unknown>> = Partial<Record<keyof TValues, string[]>>;

export interface SetFieldValueOptions {
force: boolean;
}
export interface FormActions<TValues> {
export interface FormActions<TValues extends Record<string, unknown>> {
setFieldValue<T extends keyof TValues>(field: T, value: TValues[T], opts?: Partial<SetFieldValueOptions>): void;
setFieldError: (field: keyof TValues, message: string | undefined) => void;
setErrors: (fields: Partial<Record<keyof TValues, string | undefined>>) => void;
setErrors: (fields: FormErrors<TValues>) => void;
setValues<T extends keyof TValues>(fields: Partial<Record<T, TValues[T]>>): void;
setFieldTouched: (field: keyof TValues, isTouched: boolean) => void;
setTouched: (fields: Partial<Record<keyof TValues, boolean>>) => void;
Expand Down Expand Up @@ -118,6 +121,8 @@ export interface FormContext<TValues extends Record<string, any> = Record<string
validateSchema?: (shouldMutate?: boolean) => Promise<Record<keyof TValues, ValidationResult>>;
validate(): Promise<FormValidationResult<TValues>>;
validateField(field: keyof TValues): Promise<ValidationResult>;
errorBag: Ref<FormErrorBag<TValues>>;
setFieldErrorBag(field: string, messages: string[]): void;
meta: ComputedRef<{
dirty: boolean;
touched: boolean;
Expand All @@ -130,8 +135,11 @@ export interface FormContext<TValues extends Record<string, any> = Record<string
}

export interface PublicFormContext<TValues extends Record<string, any> = Record<string, any>>
extends Omit<FormContext<TValues>, 'register' | 'unregister' | 'fields' | 'schema' | 'validateSchema'> {
errors: ComputedRef<Record<keyof TValues, string | undefined>>;
extends Omit<
FormContext<TValues>,
'register' | 'unregister' | 'fields' | 'schema' | 'validateSchema' | 'errorBag' | 'setFieldErrorBag'
> {
errors: ComputedRef<FormErrors<TValues>>;
handleReset: () => void;
submitForm: (e?: unknown) => Promise<void>;
}
36 changes: 28 additions & 8 deletions packages/vee-validate/src/useField.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
watch,
ref,
Ref,
isRef,
reactive,
computed,
Expand Down Expand Up @@ -84,6 +83,7 @@ export function useField<TValue = unknown>(
const {
meta,
errors,
errorMessage,
handleBlur,
handleInput,
resetValidationState,
Expand Down Expand Up @@ -154,10 +154,6 @@ export function useField<TValue = unknown>(
onMounted(validate);
}

const errorMessage = computed(() => {
return errors.value[0];
});

function setTouched(isTouched: boolean) {
meta.touched = isTouched;
}
Expand Down Expand Up @@ -311,7 +307,7 @@ function useValidationState<TValue>({
form?: FormContext;
type?: string;
}) {
const errors: Ref<string[]> = ref([]);
const { errors, errorMessage, setErrors } = useErrorsSource(name, form);
const formInitialValues = injectWithSelf(FormInitialValuesSymbol, undefined);
const initialValue = (getFromPath<TValue>(unref(formInitialValues), unref(name)) ?? initValue) as TValue;
const { resetMeta, meta } = useMeta(initialValue);
Expand Down Expand Up @@ -356,7 +352,7 @@ function useValidationState<TValue>({

// Updates the validation state with the validation result
function setValidationState(result: ValidationResult) {
errors.value = result.errors;
setErrors(result.errors);
meta.valid = !result.errors.length;

return result;
Expand All @@ -374,14 +370,15 @@ function useValidationState<TValue>({
} else {
value.value = newValue;
}
errors.value = state?.errors || [];

setErrors(state?.errors || []);
resetMeta(state);
}

return {
meta,
errors,
errorMessage,
setValidationState,
resetValidationState,
handleBlur,
Expand Down Expand Up @@ -462,3 +459,26 @@ function useFieldValue<TValue>(

return value as WritableRef<TValue>;
}

function useErrorsSource(path: MaybeReactive<string>, form?: FormContext) {
if (!form) {
const errors = ref<string[]>([]);
return {
errors: computed(() => errors.value),
errorMessage: computed<string | undefined>(() => errors.value[0]),
setErrors: (messages: string[]) => {
errors.value = messages;
},
};
}

const errors = computed(() => form.errorBag.value[unref(path)] || []);

return {
errors,
errorMessage: computed<string | undefined>(() => errors.value[0]),
setErrors: (messages: string[]) => {
form.setFieldErrorBag(unref(path), messages);
},
};
}
78 changes: 46 additions & 32 deletions packages/vee-validate/src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
PrivateFieldComposite,
YupValidator,
PublicFormContext,
FormErrors,
FormErrorBag,
} from './types';
import { getFromPath, isYupValidator, keysOf, resolveNextCheckboxValue, setInPath, unsetPath } from './utils';
import { FormErrorsSymbol, FormContextSymbol, FormInitialValuesSymbol } from './symbols';
Expand Down Expand Up @@ -71,25 +73,19 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
// this is important because later we need it if fields swap names
const valuesByFid: Record<string, any> = {};

// an aggregation of field errors in a map object
const errors = computed<Record<keyof TValues, string | undefined>>(() => {
return fields.value.reduce((acc, field) => {
// Check if its a grouped field (checkbox/radio)
let message: string | undefined;
const fieldName: keyof TValues = unref(field.name);
const fieldInstance = fieldsById.value[fieldName];
if (Array.isArray(fieldInstance)) {
message = unref((fieldInstance.find(f => unref(f.checked)) || field).errorMessage);
} else {
message = unref(field.errorMessage);
}
// the source of errors for the form fields
const { errorBag, setErrorBag, setFieldErrorBag } = useErrorBag(opts?.initialErrors);

if (message) {
acc[fieldName] = message;
// Gets the first error of each field
const errors = computed(() => {
return keysOf(errorBag.value).reduce((acc, key) => {
const bag = errorBag.value[key];
if (bag && bag.length) {
acc[key] = bag[0];
}

return acc;
}, {} as Record<keyof TValues, string | undefined>);
}, {} as FormErrors<TValues>);
});

// initial form values
Expand All @@ -106,28 +102,14 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
* Manually sets an error message on a specific field
*/
function setFieldError(field: keyof TValues, message: string | undefined) {
const fieldInstance: PrivateFieldComposite | PrivateFieldComposite[] | undefined = fieldsById.value[field];
if (!fieldInstance) {
return;
}

if (Array.isArray(fieldInstance)) {
fieldInstance.forEach(instance => {
instance.setValidationState({ valid: !!message, errors: message ? [message] : [] });
});
return;
}

fieldInstance.setValidationState({ valid: !!message, errors: message ? [message] : [] });
setFieldErrorBag(field, message);
}

/**
* Sets errors for the fields specified in the object
*/
function setErrors(fields: Partial<Record<keyof TValues, string | undefined>>) {
keysOf(fields).forEach(field => {
setFieldError(field, fields[field]);
});
setErrorBag(fields);
}

/**
Expand Down Expand Up @@ -425,6 +407,8 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
unregister: unregisterField,
fields: fieldsById,
values: formValues,
setFieldErrorBag,
errorBag,
schema: opts?.validationSchema,
submitCount,
validateSchema: isYupValidator(opts?.validationSchema)
Expand Down Expand Up @@ -575,7 +559,7 @@ async function validateYupSchema<TValues>(
}

if (Array.isArray(field)) {
field.forEach(f => f.setValidationState(fieldResult));
field[0].setValidationState(fieldResult);

return result;
}
Expand Down Expand Up @@ -648,3 +632,33 @@ function useFormInitialValues<TValues extends Record<string, any>>(
setInitialValues,
};
}

function useErrorBag<TValues extends Record<string, any>>(initialErrors?: FormErrors<TValues>) {
const errorBag: Ref<FormErrorBag<TValues>> = ref({});

/**
* Manually sets an error message on a specific field
*/
function setFieldErrorBag(field: keyof TValues, message: string | undefined | string[]) {
errorBag.value[field] = Array.isArray(message) ? message : message ? [message] : [];
}

/**
* Sets errors for the fields specified in the object
*/
function setErrorBag(fields: Partial<Record<keyof TValues, string | string[] | undefined>>) {
keysOf(fields).forEach(field => {
setFieldErrorBag(field, fields[field]);
});
}

if (initialErrors) {
setErrorBag(initialErrors);
}

return {
errorBag,
setErrorBag,
setFieldErrorBag,
};
}

0 comments on commit 7c13c92

Please sign in to comment.