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.
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 https://github.com/jquense/yup/issues/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'),
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.
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 https://github.com/jquense/yup/issues/97)
FYI folks this has gotten easier in the most recent version of yup. oneOf and notOneOf` now support refs
`js
validationSchema: Yup.object({
password: Yup.string().required('Password is required'),
passwordConfirm: Yup.string()
.oneOf([Yup.ref('password'), null])
.required('Password confirm is required')
})
@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]
@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
@FahdW I have a custom error set. I just posted the minimal implementation.
@vuhrmeister Yeah the oneOf is not working i had to add my own method to get it to work
looks like a bug, 'ill take a look
@jquense Doesn't work for my either, please take a look
tried mixed().oneOf ... string().oneOf ... nothing seems to work to match 2 fields :(
works with [email protected]
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.
@janzenz just specify custom error message
.oneOf([ref('password')], 'Passwords do not match')
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
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;
})
})
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
}
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')
})
For me that is not working. Did anything regarding
refs change?@eonwhite I'm using your code, but I don't get the link.
valueis the actual value of the prop the test is running on. Butthis.resolve(ref)returns aStringSchema. 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.
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
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!')
});
initialValues={{
email: '',
password:'',
passwordConfirmation:''
}}
validate={values => {
const errors = {};
if (values.password != values.passwordConfirmation) {
errors.passwordConfirmation = "Passwords do not match"
}
return errors;
}}
.............the rest
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.
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')
})
any advancement made? nothing above mentioned work for me...!
@IZUNA894 Hey, does this help vvv ?
If anyone is attempting to validate a
confirmPasswordagainst a schema that's conditional on thepasswordbeing given thenYup'swhenmethod might be helpful. https://github.com/jquense/yup#mixedwhenkeys-string--arraystring-builder-object--value-schema-schema-schema
Most helpful comment
FYI folks this has gotten easier in the most recent version of yup.
oneOfand notOneOf` now support refs`
js validationSchema: Yup.object({ password: Yup.string().required('Password is required'), passwordConfirm: Yup.string() .oneOf([Yup.ref('password'), null]) .required('Password confirm is required') })