How to set up yup validation conditionals? - javascript

I have a set of data that i'm tring to validate; It have two fields: "citiesProvided" and "servicesProvided" they are dependent of "LegalCorrespondenceService" that is a array with just one string with the value "true" or "false" Like represented below:
Structure of the data
When "true" the validation of the other fields need to be done, otherwise with "false" not.
How can i do this conditional validation with yup?
this the code that I had no result:
const schema = Yup.object().shape({
JuridicCorrespondence: Yup.array().of(
Yup.object().shape({
LegalCorrespondenceService: Yup.array()
.of(Yup.boolean())
.length(1, 'Escolha uma opção.'),
citiesProvided: Yup.array().when(['LegalCorrespondenceService'], {
is: 'true',
then: Yup.array()
.of(Yup.string())
.length(1, 'Escolha uma opção.')
}),
servicesProvided: Yup.array().when(['LegalCorrespondenceService'], {
is: 'true',
then: Yup.array()
.of(Yup.string())
.length(1, 'Escolha uma opção.')
}),
})
)
});

Related

Raise validation when recive extra key in yup object

I am using yup for object validations. I have the following schema
const schema = yup.object().shape({
name: yup.string().required(),
});
I am validating it with object
{
"name": "Foo",
"desc": "Lorem ipsum"
}
Yup validates this object although it has an extra key desc. I want to raise the error for extra keys in the object.
I have tried with abortEarly and stripUnknown in .validate but it doesn't work.
schema.validateSync(data, { strict: true, stripUnknown: true })
You need to append the .strict() to the object you are validating. This makes the validation fail and then you can handle the error however you wish to do that.
So, in your case, change your schema to this:
const schema = yup.object().shape({
name: yup.string().required()
}).noUnknown(true).strict();
await schema.validate(data, { abortEarly: false });

Rename yup error key based on other value

I have an array of objects which contain the same values including locale.
Locale is a language: en, de
I have this schema:
descriptions: yup.object().shape({
data: yup.array().of(
yup.object().shape({
name: yup.string().required('required'),
locale: yup.string().oneOf(['de', 'en']),
})
),
})
I want to get errors returned like this:
{ descriptions: { data: [{ name_en:"required"}, ...] } }
I tried to do it with .label() but, it returns the same key or I'm doing it wrong.
Also I tried with transformKeys like this:
.transformKeys(key => `${key}_something`)
Even if this works, I still need access to the locale value

Access to required fields with Yup

I use react-hook-form with yup to validate my forms.
I want to know all required fields of a schema to display some information in form (like '*' for required fields).
We could achieve this with this line of code :
schema.describe().fields[field].tests.findIndex(({ name }) => name === 'required'
However, this code doesn't work for conditional validation.
Schema example :
const schema = yup.object().shape({
email: yup
.string()
.email()
.required(),
isProfileRequired: yup
.boolean(),
profile: yup
.object()
.when('isProfileRequired',{
is: (isProfileRequired) => isProfileRequired,
then:
yup
.object()
.nullable()
.required()
})
})
Is there a way to retrieve this informations within the form ?
There is actually no "nice" way to do it but this works:
function isRequired(field){
return schema.fields[field]._exclusive.required || false
}
Notice: schema.fields[field]._exclusive.required returns true if required or undefined.
Testing exclusiveTests instead of _exclusive worked for me.
const isRequired =
validationSchema?.fields[aField.name]?.exclusiveTests?.required || false;

Yup how to validate mandatory fields on null object

I have a problem regarding the validation of mandatory fields which are properties of a (possibly) null object.
Here is an example schema :
object().shape({
catalog: {
brand: string().required()
}
})
If I try to validate the following object against this schema, I get the expected error : brand is required. From what I understood, there is a default value created for undefined object which mimics the shape of the object in the schema. This is the behavior I expected and what I want.
{ catalog: undefined }
// "catalog.brand is a required field"
But in my case, I don't receive an object with undefined, but with null. And I can't figure out how to manage the same result with a null value.
{ catalog: null }
// No error on catalog.brand
Manually casting null to undefined is out of the question as there is a good reason why I receive null.
Here is a codesandbox which reproduces my use case :
https://codesandbox.io/s/yup-playground-tbfcj
I would really appreciate a bit of help on this, thanks :)
The solution is to include both nullable() and required() to the shape.
Example
const requiredOption = Yup.object()
.shape({
value: Yup.string(),
label: Yup.string(),
})
.nullable()
.required('This field is required.');
Try adding strict() on your object schema
object().shape({
catalog: {
brand: string().required()
}
}).strict();
Here's a less mindbending way to do it than the other answer. This allows you to keep the required directive on the subfield whether or not the parent schema/field containing it is empty at validation time.
yup.object().shape({
field: yup.object().shape({
subfield: string().required(),
}).default(undefined),
});
// Or...
yup.object().shape({
field: yup.object().shape({
subfield: string().required(),
}).default(null).nullable(),
});
One difference between the two is that when you validate data over each of these schemas and field is missing, if you also pass in stripUnknown, in the first case the field will be totally removed from the validated result, but in the second case the field will still show up but with value null.
Adding the .default(null) method along with .nullable() worked for me.
const parentValidator = Yup.object({
personData: personDataValidator,
data: Yup.object({
workshift: Yup.array()
.of(Yup.string().matches(/x|-/g, "Local de trabalho inválido"))
.required("Turno de trabalho obrigatório")
.typeError("Turno de trabalho inválido"),
workplace: Yup.string()
.required("Local de trabalho obrigatório")
.typeError("Local de trabalho inválido"),
email: Yup.string()
.required("Email obrigatório")
.typeError("Email inválido"),
}),
})
.default(null)
.nullable();

Conditional Validation in Yup

I have an email field that only gets shown if a checkbox is selected (boolean value is true). When the form get submitted, I only what this field to be required if the checkbox is checked (boolean is true).
This is what I've tried so far:
const validationSchema = yup.object().shape({
email: yup
.string()
.email()
.label('Email')
.when('showEmail', {
is: true,
then: yup.string().required('Must enter email address'),
}),
})
I've tried several other variations, but I get errors from Formik and Yup:
Uncaught (in promise) TypeError: Cannot read property 'length' of undefined
at yupToFormErrors (formik.es6.js:6198)
at formik.es6.js:5933
at <anonymous>
yupToFormErrors # formik.es6.js:6198
And I get validation errors from Yup as well. What am I doing wrong?
You probably aren't defining a validation rule for the showEmail field.
I've done a CodeSandox to test it out and as soon as I added:
showEmail: yup.boolean()
The form started validation correctly and no error was thrown.
This is the url: https://codesandbox.io/s/74z4px0k8q
And for future this was the correct validation schema:
validationSchema={yup.object().shape({
showEmail: yup.boolean(),
email: yup
.string()
.email()
.when("showEmail", {
is: true,
then: yup.string().required("Must enter email address")
})
})
}
Formik author here...
To make Yup.when work properly, you would have to add showEmail to initialValues and to your Yup schema shape.
In general, when using validationSchema, it is best practices to ensure that all of you form's fields have initial values so that Yup can see them immediately.
The result would look like:
<Formik
initialValues={{ email: '', showEmail: false }}
validationSchema={Yup.object().shape({
showEmail: Yup.boolean(),
email: Yup
.string()
.email()
.when("showEmail", {
is: true,
then: Yup.string().required("Must enter email address")
})
})
}
/>
You can even use a function for complex cases . Function case helps for complex validations
validationSchema={yup.object().shape({
showEmail: yup.boolean(),
email: yup
.string()
.email()
.when("showEmail", (showEmail, schema) => {
if(showEmail)
return schema.required("Must enter email address")
return schema
})
})
}
Totally agree with #João Cunha's answer. Just a supplement for the use case of Radio button.
When we use radio button as condition, we can check value of string instead of boolean. e.g. is: 'Phone'
const ValidationSchema = Yup.object().shape({
// This is the radio button.
preferredContact: Yup.string()
.required('Preferred contact is required.'),
// This is the input field.
contactPhone: Yup.string()
.when('preferredContact', {
is: 'Phone',
then: Yup.string()
.required('Phone number is required.'),
}),
// This is another input field.
contactEmail: Yup.string()
.when('preferredContact', {
is: 'Email',
then: Yup.string()
.email('Please use a valid email address.')
.required('Email address is required.'),
}),
});
This the radio button written in ReactJS, onChange method is the key to trigger the condition checking.
<label>
<input
name="preferredContact" type="radio" value="Email"
checked={this.state.preferredContact == 'Email'}
onChange={() => this.handleRadioButtonChange('Email', setFieldValue)}
/>
Email
</label>
<label>
<input
name="preferredContact" type="radio" value="Phone"
checked={this.state.preferredContact == 'Phone'}
onChange={() => this.handleRadioButtonChange('Phone', setFieldValue)}
/>
Phone
</label>
And here's the callback function when radio button get changed. if we are using Formik, setFieldValue is the way to go.
handleRadioButtonChange(value, setFieldValue) {
this.setState({'preferredContact': value});
setFieldValue('preferredContact', value);
}
email: Yup.string()
.when(['showEmail', 'anotherField'], {
is: (showEmail, anotherField) => {
return (showEmail && anotherField);
},
then: Yup.string().required('Must enter email address')
}),
it works for me very well :
Yup.object().shape({
voyageStartDate:Yup.date(),
voyageEndDate:Yup.date()
.when(
'voyageStartDate',
(voyageStartDate, schema) => (moment(voyageStartDate).isValid() ? schema.min(voyageStartDate) : schema),
),
})
I use yup with vee-validate
vee-validate
here is the sample code from project
const schema = yup.object({
first_name: yup.string().required().max(45).label('Name'),
last_name: yup.string().required().max(45).label('Last name'),
email: yup.string().email().required().max(255).label('Email'),
self_user: yup.boolean(),
company_id: yup.number()
.when('self_user', {
is: false,
then: yup.number().required()
})
})
const { validate, resetForm } = useForm({
validationSchema: schema,
initialValues: {
self_user: true
}
})
const {
value: self_user
} = useField('self_user')
const handleSelfUserChange = () => {
self_user.value = !self_user.value
}

Categories