Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yup schema.validate() options, show every error of a field at the same time #243

Open
acezard opened this issue Nov 9, 2017 · 33 comments
Open

Comments

@acezard
Copy link

acezard commented Nov 9, 2017

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(/georges.abitbol@gmail.com/, '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

@clentfort
Copy link

@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.

@stale
Copy link

stale bot commented Aug 15, 2018

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.

@stale stale bot added the stale label Aug 15, 2018
@umpc
Copy link

umpc commented Aug 15, 2018

:/

@stale stale bot removed the stale label Aug 15, 2018
@Tsymalyi
Copy link

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.

@woile
Copy link

woile commented Aug 26, 2018

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?

@stale
Copy link

stale bot commented Oct 25, 2018

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.

@stale stale bot added the stale label Oct 25, 2018
@umpc
Copy link

umpc commented Oct 26, 2018

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.

@stale stale bot removed the stale label Oct 26, 2018
@egemon
Copy link

egemon commented Dec 27, 2018

@jaredpalmer could u help us please?

@DrJacobHolden
Copy link

Would love to see this become an option.

@mbrowne
Copy link

mbrowne commented May 31, 2019

New PR for the new version of Formik (which is quite different since it now uses hooks):
#1573

@tusharpari
Copy link

tusharpari commented Jul 1, 2019

ght it

I also need to acheive the same, but not able to make it, can you please share the code pen example ?
Thanks

@KrzysztofMadejski
Copy link

This issue is also related: #750

@KrzysztofMadejski
Copy link

KrzysztofMadejski commented Oct 8, 2019

@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

formik/src/Formik.tsx

Lines 216 to 217 in 24ac8dd

if (err.name === 'ValidationError') {
resolve(yupToFormErrors(err));

@dvakatsiienko
Copy link

Agree that we need an option to get a list of all current validation errors at the same time.

@acezard
Copy link
Author

acezard commented Mar 4, 2020

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

@usama093
Copy link

@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]

@deathemperor
Copy link

deathemperor commented Aug 24, 2020

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

@dvakatsiienko
Copy link

dvakatsiienko commented Aug 24, 2020

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).

@lamasagar
Copy link

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?

@uneet7
Copy link

uneet7 commented Oct 16, 2020

@jaredpalmer please post a good work around, if any?

Repository owner deleted a comment from mbrowne Oct 16, 2020
@jaredpalmer
Copy link
Owner

The solution is to use a custom validate prop for now. We’ll reevaluate in v3

@mbrowne
Copy link

mbrowne commented Oct 16, 2020

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.

@LyricL-Gitster
Copy link

If anyone hits this looking for a stopgap until Formik allows schema options, check this out.

@tobiaswaelde
Copy link

tobiaswaelde commented Apr 5, 2021

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;
}

@dguglielmi-git
Copy link

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);
			}
		},
	});

@ankitkhaire20
Copy link

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

@niketanmoon
Copy link

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!

@lindeberg
Copy link

The solution is to use a custom validate prop for now. We’ll reevaluate in v3

Got any examples of that?

@uptonm
Copy link

uptonm commented Nov 29, 2021

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']
}

@hancush
Copy link

hancush commented Jul 28, 2023

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
    }, {})
  })
}}

@Akarshit7
Copy link

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
    }, {})
  })
}}

where to use this validate function

@TrevPennington
Copy link

TrevPennington commented Jan 29, 2024

@Akarshit7 You use it as a prop on <Formik validate={...validate}> wrapper component

@AlexMachin1997
Copy link

@Akarshit7 That worked great thanks 😄 Just got to find a way to override the types now 🙃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests