Reputation: 924
I'm using formik for form management in reactjs, i have a question on validation with yup.
I have two fields, ones is a select control to select the country, and the other one is a zipcode.
In the country array we have the regex to validate the zipcode, and the idea is to validate the entered zipcode using the regex of the currently selected country, someone can give a clue on how to do this.
Upvotes: 44
Views: 114473
Reputation: 1125
An example of a field that requires a numeric value that cannot be higher than the multiplication of two other field's values
const validationSchema = Yup.object().shape({
num1: Yup.number().positive().required('This field is required.'),
num2: Yup.number().positive().required('This field is required.'),
num3: Yup.number().positive().required('This field is required.')
.when(['num1', 'num2'], (num1, num2, schema) => {
return num1 > 0 && num2 > 0 ? schema.max(num1 / num2) : schema.max(0);
})
});
Upvotes: 60
Reputation: 4565
You can do conditional validation based on an array of fields.
Example below will make field6
required only if at least one of field1 - field5
is not specified.
let schema = object({
field1: Yup.bool(),
field2: Yup.bool(),
field3: Yup.bool(),
field4: Yup.bool(),
field5: Yup.bool(),
field6: Yup.bool().when(
[
'field1',
'field2',
'field3',
'field4',
'field5',
],
{
is: (...fields) => fields.some(Boolean),
then: Yup.bool().notRequired(),
otherwise: Yup.bool().required(),
}
),
});
Upvotes: 1
Reputation: 1384
You can access value of any form field by destructuring parent like const { country } = this.parent;
const yup = require("yup");
const schema = yup.object({
country: yup.object().required("This field is required"),
zipcode: yup
.string()
.required("This field is required")
.test("is-right-zipcode", "Invalid zipcode", function(code) {
const { country } = this.parent;
return code.match(country.regex);
})
});
Upvotes: 11
Reputation: 17150
Here's an example of enforcing a datepicker's end date being after
the start date:
const schema = Yup.object().shape({
start_date: Yup.date()
.typeError('Start Date is required')
.required('Start Date is required'),
end_date: Yup.date()
.typeError('End Date is required')
.required('End Date is required')
.when('start_date', (start_date) => {
if (start_date) {
return Yup.date()
.min(start_date, 'End Date must be after Start Date')
.typeError('End Date is required')
}
}),
})
It was required to put the if
statement to validate correctly while the form was loading, and it was also required to put the typeError
rule in to validate correctly when the start date was selected but the end date wasn't yet.
The important thing is you can see usage of when
; the first param is the field to check against, and the second param is the validation function which returns a Yup validation object.
I tried to just return true or false, but it seemed to throw errors, so it's a bit more complex than it just being a pure validation function.
Upvotes: 14
Reputation: 1529
I had the exact same problem. I was a able to use when
to solve it.
import { object, string } from 'yup';
const validCountries = ['US', 'CA'];
const zipRegexes = {
US: /^\d{5}(?:-?\d{4})?$/,
CA: /^[ABCEGHJKLMNPRSTVXY]\d[A-Z]\d[A-Z]\d$/
};
const addressSchema = object().shape({
country: string()
.oneOf(validCountries, 'Please select a country from the list above')
.required('Please select a country from the list above'),
/* Other fields
.
.
.
*/
zip: string()
.trim()
.required('Required')
.transform(value => value.toUpperCase())
.when('country', (country, schema) => {
if (
string()
.oneOf(validCountries)
.required()
.isValid(country)
) {
return schema.matches(
zipRegexes[country],
`not a valid ${country} zip code`
);
}
return schema;
})
});
Upvotes: 7
Reputation: 67
let schema = object({
isBig: boolean(),
count: number()
.when('isBig', {
is: true, // alternatively: (val) => val == true
then: yup.number().min(5),
otherwise: yup.number().min(0),
})
});
Upvotes: 4