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

Matching fields example (ie. Passwords) #90

Closed
mwickett opened this issue Jul 25, 2017 · 27 comments
Closed

Matching fields example (ie. Passwords) #90

mwickett opened this issue Jul 25, 2017 · 27 comments

Comments

@mwickett
Copy link

I'm struggling with getting a Yup validation working to ensure two fields match (in this case, password and password confirm).

    validationSchema: Yup.object().shape({
      password: Yup.string()
        .required('Password is required'),
      passwordConfirm: Yup.mixed().test('match', 'Passwords do not match', function (password) {
        return password === this.parent.passwordConfirm
      }).required('Password confirm is required')
    }),

I'm new to Formik & Yup (and relatively new to JS generally). Really appreciate any pointers.

@eonwhite
Copy link
Collaborator

Yup doesn't make this common usecase as easy as it should. Here's how I've gotten this to work (a slight modification of code in jquense/yup#97)

function equalTo(ref: any, msg: any) {
  return Yup.mixed().test({
    name: 'equalTo',
    exclusive: false,
    message: msg || '${path} must be the same as ${reference}',
    params: {
      reference: ref.path,
    },
    test: function(value: any) {
      return value === this.resolve(ref);
    },
  });
}
Yup.addMethod(Yup.string, 'equalTo', equalTo);

...

then in your validationSchema:

    passwordConfirm: Yup.string().equalTo(Yup.ref('password'), 'Passwords must match').required('Required'),

@mwickett
Copy link
Author

Thanks so much for this. I see how it's more complicated than I had originally thought, although this all makes sense. Appreciate you taking the time.

@vuhrmeister
Copy link

vuhrmeister commented Jan 2, 2018

For me that is not working. Did anything regarding refs change?

@eonwhite I'm using your code, but I don't get the link. value is the actual value of the prop the test is running on. But this.resolve(ref) returns a StringSchema. So this comparison doesn't work.

I already searched for that issue but I didn't find any working solution to get the actual value of the ref.

*edit: I just saw this issue is posted for formik, so probably not the best place to answer if anything changed. But maybe someone has an idea though. (I came from jquense/yup#97)

@jquense
Copy link
Contributor

jquense commented Jan 2, 2018

FYI folks this has gotten easier in the most recent version of yup. oneOf and notOneOf` now support refs

validationSchema: Yup.object({
  password: Yup.string().required('Password is required'),
  passwordConfirm: Yup.string()
     .oneOf([Yup.ref('password'), null])
     .required('Password confirm is required')
})
``

@vuhrmeister
Copy link

vuhrmeister commented Jan 2, 2018

@jquense In 0.23.0? Doesn't work for me with .oneOf([Yup.ref('password')]). Also ${values} in message string just outputs [object Object]. The complete message is

newPasswordRepeat must be one of the following values: [object Object]

@FahdW
Copy link

FahdW commented Jan 11, 2018

@jquense I found that the validation is incorrectly showing passwords that do in fact match as an error.
@vuhrmeister Make sure to write an error for it yup.string().oneOf([yup.ref('password'), null], "Passwords don't match") I also have an issue where it is not validating properly

@vuhrmeister
Copy link

@FahdW I have a custom error set. I just posted the minimal implementation.

@FahdW
Copy link

FahdW commented Jan 11, 2018

@vuhrmeister Yeah the oneOf is not working i had to add my own method to get it to work

@jquense
Copy link
Contributor

jquense commented Jan 11, 2018

looks like a bug, 'ill take a look

@lednhatkhanh
Copy link

@jquense Doesn't work for my either, please take a look

@rares-lupascu
Copy link

tried mixed().oneOf ... string().oneOf ... nothing seems to work to match 2 fields :(

@rares-lupascu
Copy link

works with yup@0.24.0

@janzenz
Copy link

janzenz commented May 26, 2018

oneOf's error message doesn't seem to make sense. Instead of confirming the 2 fields match, instead it gives me this error:

confirm_password must be one of the following values: , Ref(password)

Following @eonwhite solution is more correct.

@tushar-singh
Copy link

@janzenz just specify custom error message

.oneOf([ref('password')], 'Passwords do not match')

@fahadakhan96
Copy link

This doesn't work if I want to allow the password field to be empty.

password: Yup.lazy(
                    value =>
                      !value
                        ? Yup.string()
                        : Yup.string()
                            .min(6, 'Password must be at least 6 characters')
                            .required('Password is required'),
                  ),
confirmPassword: Yup.string().oneOf(
                    [Yup.ref('password')],
                    'Passwords do not match',
                  ),

The schema returns valid if password is not empty and confirmPassword is empty

@whatifif
Copy link

whatifif commented Sep 17, 2018

I found that there is no 'equalTo' method in yup. This is a convenient method needed by everyone.
yup needs to include this 'equalTo' method as a basic one.

yup.addMethod(yup.mixed, 'equalTo', function(ref, message) {
    const msg = message || '${path} should match ${ref.path}';
    return this.test('equalTo', msg, function (value) {
      let refValue = this.resolve(ref);
      return !refValue || !value || value === refValue;
    })
})

@logistus
Copy link

I used manually triggering validation.

Added validate attribute to the password confirmation Field:

...
<Field type="password" name="password" id="password" />
<Field type="password" name="password_confirm" id="password_confirm" validate={validatePassword} />
...

and validatePassword function:

const validatePassword = (password) => {
  let error
  if (!password) {
    error = "Confirm password"
  }
  else if (password !== document.getElementById("password").value) {
    error = "Passwords do not match"
  }
  return error
}

@telmotrooper
Copy link

I just wanted to point out that if you want to display a custom error message based on @jquense 's answer, you'd do it like this:

validationSchema: Yup.object({
  password: Yup.string().required('Password is required'),
  passwordConfirm: Yup.string()
     .oneOf([Yup.ref('password'), null], "Passwords must match")
     .required('Password confirm is required')
})

@olezt
Copy link

olezt commented May 16, 2019

For me that is not working. Did anything regarding refs change?

@eonwhite I'm using your code, but I don't get the link. value is the actual value of the prop the test is running on. But this.resolve(ref) returns a StringSchema. So this comparison doesn't work.

I already searched for that issue but I didn't find any working solution to get the actual value of the ref.

*edit: I just saw this issue is posted for formik, so probably not the best place to answer if anything changed. But maybe someone has an idea though. (I came from jquense/yup#97)

Just in case some has this problem.
For some reason test: value => {} results to the problem you mention while test: function(value) {} works fine.

@john-raymon
Copy link

If anyone is attempting to validate a confirmPassword against a schema that's conditional on the password being given then Yup's when method might be helpful. https://github.com/jquense/yup#mixedwhenkeys-string--arraystring-builder-object--value-schema-schema-schema

@BrutalHex
Copy link

just add validate to Fomatik tag,it works like a charm.here is a working sample:

const schema = yup.object({
email: yup.string()
.email('E-mail is not valid!')
.required('E-mail is required!'),
password: yup.string()
.min(6, 'Password has to be longer than 6 characters!')
.required('Password is required!'),
passwordConfirmation: yup.string()

            .required('Password confirmation is required!')

    });

<Formik
validationSchema={schema}
initialValues={{
email: '',
password:'',
passwordConfirmation:''
}}
validate={values => {
const errors = {};

                                      if (values.password != values.passwordConfirmation) {
                                        errors.passwordConfirmation = "Passwords do not match"
                                    }
                                     
                                    return errors;
                                  }}
                                  .............the rest

@deanc
Copy link

deanc commented Feb 5, 2020

If anyone is looking for a solution to validate that two fields are the same ONLY when the first one is filled in then here it is. The use-case here is a naive update profile page which only wants to validate the password is being changed if you fill in the field:

  password: Yup.string()
    .min(8)
    .max(128),
  confirmpassword: Yup.string().when("newpassword", {
    is: val => val && val.length > 0,
    then: Yup.string()
      .oneOf([Yup.ref("newpassword")], "Both passwords need to be the same")
      .required()
  }),

As you can see the password field is not required. But as soon as its filled in the confirmpassword field becomes required and must be equivalent to the password field. The key thing here that I had not seen before is the addition of the required() validator inside the then. The is condition is not enough.

@rcmirandilla
Copy link

This works for me for a simple password confirmation

const validationSchema = Yup.object().shape({
  username: Yup.string().required('Required'),
  firstName: Yup.string().required('Required'),
  lastName: Yup.string().required('Required'),
  email: Yup.string().email('Invalid email').required('Required'),
  contactNumber: Yup.number().required('Required'),
  password: Yup.string().required('Required'),
  repassword: Yup.string().oneOf([Yup.ref('password')],'Password does not match').required('Required')
})

@IZUNA894
Copy link

any advancement made? nothing above mentioned work for me...!

@john-raymon
Copy link

@IZUNA894 Hey, does this help vvv ?

If anyone is attempting to validate a confirmPassword against a schema that's conditional on the password being given then Yup's when method might be helpful. https://github.com/jquense/yup#mixedwhenkeys-string--arraystring-builder-object--value-schema-schema-schema

@AustinGil
Copy link

AustinGil commented Mar 14, 2021

Just going to put this out there, but why not something like:

const schema = yup.object({
  password: yup.string().required(),
  repeatPassword: yup.string().matches(objectToTest.password).required()
})

@genefever
Copy link

genefever commented Jul 10, 2021

This schema works well if you want to validate that both passwords match and then show the green checkmarks to show that they both match:

Validation Schema:

const schema = yup.object({
  password: yup.string().required('Password is required'),
  confirmPassword: yup.string()
    .test('passwords-match', 'Passwords must match', function(value){
      return this.parent.password === value
    })
})

Show Green Checkmarks in Password Fields if they Both Match:

Add this to each of the password fields' props to make the green checkmarks appear if both passwords match (make sure to include {errors, values} in the Formik parameters):

In the password textfield props:

isValid={
    !errors.confirmPassword &&
    values.password
}

...

In the confirmPassword textfield props:

isValid={
    !errors.confirmPassword &&
    values.confirmPassword
}

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

No branches or pull requests