formik
formik copied to clipboard
Yup schema.validate() options, show every error of a field at the same time
Hi, sorry I didn't find another issue related to this problem. And since yup has first class support I thought it would be justified to ask about it.
So I'm trying to set an option, mainly here the abortEarly option. My goal is to have a password field with multiple errors displayed and it seems it was the only way. Couldn't find how to do that with validateSchema()
So I did the following instead:
validate: (values) => {
const schema = Yup.object().shape({
email: Yup.string()
.matches(/[email protected]/, 'cant change email'),
providerName: Yup.string()
.required('type your name'),
password: Yup.string()
.min(8, 'at least 8 chars')
.matches(/[a-z]/, 'at least one lowercase char')
.matches(/[A-Z]/, 'at least one uppercase char')
.matches(/[a-zA-Z]+[^a-zA-Z\s]+/, 'at least 1 number or special char (@,!,#, etc).'),
passwordConfirm: Yup.string()
.equalTo(Yup.ref('password'), 'passwords don't match')
})
return schema.validate(values, { abortEarly: false })
.then(() => {})
.catch((err) => {
throw err
})
}
This way I can get every error of a single field and map them to my components to display every error of a field at the same time. Isn't there a cleaner way ? Also I think it could be a good use case to showcase ? Thanks
@jaredpalmer I'm also facing this problem. Would you be open to a validationSchemaOptions-property on the Formik-component? If so Id' be willing to provide a PR.
Hola! So here's the deal, between open source and my day job and life and what not, I have a lot to manage, so I use a GitHub bot to automate a few things here and there. This particular GitHub bot is going to mark this as stale because it has not had recent activity for a while. It will be closed if no further activity occurs in a few days. Do not take this personally--seriously--this is a completely automated action. If this is a mistake, just make a comment, DM me, send a carrier pidgeon, or a smoke signal.
:/
I have the similar problem. I need way to manage yup validation options Now when i use several rules for one field they all called end return last error message.
I'm also interested in this, I would like to know what are the proposed solutions. I'd like to have something like:
errors = {
name: [
"Too short",
"Cannot contain numbers",
]
}
Is it possible?
Hola! So here's the deal, between open source and my day job and life and what not, I have a lot to manage, so I use a GitHub bot to automate a few things here and there. This particular GitHub bot is going to mark this as stale because it has not had recent activity for a while. It will be closed if no further activity occurs in a few days. Do not take this personally--seriously--this is a completely automated action. If this is a mistake, just make a comment, DM me, send a carrier pidgeon, or a smoke signal.
Comments here keep getting upvotes, so I'm going to post this and hopefully keep this issue open for addressing it in the near future.
@jaredpalmer could u help us please?
Would love to see this become an option.
New PR for the new version of Formik (which is quite different since it now uses hooks): https://github.com/jaredpalmer/formik/pull/1573
ght it
I also need to acheive the same, but not able to make it, can you please share the code pen example ? Thanks
This issue is also related: https://github.com/jaredpalmer/formik/issues/750
@acezard solution don't work for me. Yup returns a ValidationError with packed errors, while Formik expects object with keys.
yupToFormErrors(err) mapping should be called , but it is internal function
https://github.com/jaredpalmer/formik/blob/24ac8ddbddc5717c57178b8a9e90f752fbaa6858/src/Formik.tsx#L216-L217
Agree that we need an option to get a list of all current validation errors at the same time.
Yeah my solution is 3 years old at this point @KrzysztofMadejski, I would not be surprised if it's broken by now.
Sadly it seems this issue has been neglected, but I don't blame the formik owner, open source is open source.
I guess my ticket could be closed maybe, I don't know. Formik has changed a lot since then
@acezard This may be late, But there is a property name inner that contain path & error message
err.inner.path will return something like user.[property]
Showing multiple errors at the same time is more friendly. Especially in case of setting a password. Without it, it's bad impression for new users. I really think this need more attention.
@jaredpalmer
Showing multiple errors at the same time is more friendly. Especially in case of setting a password for new users. Without it, it's bad impression for new users. I really think this need more attention.
@jaredpalmer
100%. I have the same situation (create password validation).
Has anyone found any solution to this? I'm having a similar issue. While creating a password, I want to know which options passed and which options failed and display the errors on the UI. Is it possible with Formik and Yup or is there any alternatives?
@jaredpalmer please post a good work around, if any?
The solution is to use a custom validate prop for now. We’ll reevaluate in v3
Weird that my comment got deleted; I guess it's not allowed to mention alternative libraries here. Good news that this feature will be considered for v3 though.
If anyone hits this looking for a stopgap until Formik allows schema options, check this out.
I wrote a small function to convert the yup error into an error object:
import { ValidationError } from 'yup';
type ErrorObject = {
[field: string]: string[];
};
/**
* Convert yup error into an error object where the keys are the fields and the values are the errors for that field
* @param {ValidationError} err The yup error to convert
* @returns {ErrorObject} The error object
*/
export function yupErrorToErrorObject(err: ValidationError): ErrorObject {
const object: ErrorObject = {};
err.inner.forEach((x) => {
if (x.path !== undefined) {
object[x.path] = x.errors;
}
});
return object;
}
Hello, I used validateOnChange: false, into the useFormik() hook.
Attached the code:
const formik = useFormik({
initialValues: initialValues(),
validateOnChange: false,
validationSchema: Yup.object({
email: Yup.string().email(t('loginFormWrongEmail')).required(t('loginFormEmailRequired')),
password: Yup.string().required(t('loginFormPassRequired')),
}),
onSubmit: async (formData) => {
setError('');
try {
const { data } = await login({
variables: {
input: formData,
},
});
const { token } = data.login;
setToken(token);
setUser(decodeToken(token));
} catch (error) {
setError(error.message);
}
},
});
Hi,I need help on One scenario initially, I need submit button to disable so I use the ValidateOnMount it helps me out for the same. The issue is when start typing the text it did not display the error message.
code for the error message
<TextInput style={styles.inputdata} placeholder="Please Enter your Email" placeholderTextColor="#8b9cb5" value={values.email} onChangeText={handleChange('email')} returnKeyType={"next"} onBlur={handleBlur('email')} autoCapitalize="none" />
{touched.email && errors.password ?
(< Text style={styles.errorText}>{errors.email}</Text>)
: null
}
Formik
validationSchema={loginValidationSchema} initialValues={{ email: '', password: '' }} validateOnMount={true}
validateOnChange={false}
onSubmit={(values,) => {
}}
can any One Help me out on these
Step 1: Paste this out of your function
const asyncValidateSchema = (schema) => (values) => schema .validate(values, { abortEarly: false, strict: false, }) .then(() => ({})) .catch(({ inner }) => inner.reduce( (memo, { path, message }) => ({ ...memo, [path]: (memo[path] || []).concat(message), }), {} ) );
Step 2: Change Formik validation schema to validate
<Formik enableReinitialize={true} onSubmit={(values, actions) => handleSubmit(values, actions) } initialValues={defaultState} validate={asyncValidateSchema(validationSchema)} >
Step 3: Now add validation schema outside the function
const validationSchema = yup.object().shape({ new_password: yup .string() .required() .min(8, "At least 8 characters") .upperCaseCheck() .numberCheck() .specialCharacterCheck() .lowerCaseCheck(), re_new_password: yup .string() .required() .oneOf( [yup.ref("new_password"), null], "The password you entered does not match" ), });
I have added some validation scenarios for password
yup.addMethod(yup.string, "upperCaseCheck", function (errorMessage) { return this.test(oneUpperCase, errorMessage, function (value) { const { path, createError } = this; // change regex here to make other functions if (!/(?=.*[A-Z])/.test(value)) { return createError({ message: At least one uppercase letter, path: path, // Fieldname }); } return true; }); });
Now you will get a list of errors inside errors.new_password.
Hope this helps.
Cheers!
The solution is to use a custom validate prop for now. We’ll reevaluate in v3
Got any examples of that?
const asyncValidateSchema = (schema) => (values) => schema .validate(values, { abortEarly: false, strict: false, }) .then(() => ({})) .catch(({ inner }) => inner.reduce( (memo, { path, message }) => ({ ...memo, [path]: (memo[path] || []).concat(message), }), {} ) );
For anyone interested in using this solution with either TypeScript or with nested properties within formik using the . syntax (i.e. parent_obj.formKey). I ended up utilizing lodash's set function to set those nested keys and ensure that formik can understand the returned errors object, resulting in the following:
function FormikAsyncValidateSchema<FormState extends object>(
schema: Yup.ObjectSchema<any>
): ( values: FormState ) => Promise<FormikErrors<FormState>> {
return async function ( values: FormState ): Promise<FormikErrors<FormState>> {
try {
await schema.validate( values, { abortEarly: false, strict: false, } );
return Promise.resolve( {} );
} catch ( yupErr ) {
return yupErr.inner.reduce( ( memo, { path, message } ) => {
return set( memo, path, ( memo[path] || [] ).concat( message ) );
}, {} );
}
};
}
this results in the returned object looking like this:
const errors = {
parent_obj: {
childFormKey: ['Child Form Key is required']
}
}
instead of:
const errors = {
"parent_obj.childFormKey": ['Child Form Key is required']
}
Here's how I addressed:
validate={(values) => {
// Return all errors at once
return validationSchema.validate(values, { abortEarly: false })
.then(() => {})
.catch((err) => {
return err.inner.reduce((obj, e) => {
if (!(e.path in obj)) obj[e.path] = []
obj[e.path] = obj[e.path].concat(e.errors)
return obj
}, {})
})
}}