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();
Related
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 });
I have object types that have a 'translations' property where the fields that can be translated into different languages are passed into the specific 'lang' property as you can see in the schema below.
An English translation is always required and the rest of the languages are optional, I can achieve this by setting .default(undefined) to the optional languages.
When a language is present and the validation for its inner fields fails, the error is always associated to the field itself ('name' in this case). This behaviour is expected.
What I else want to achieve and I don't know how is to show an error when the 'translations' property 'en' is not present with a message like 'An English translation is required'.
const categoryTranslationsSchema = object({
name: string()
.min(3, 'Must have at least 3 characters.')
.max(16, 'Cannot be longer than 16 characteres.')
.required('Must provide a name.')
})
const categorySchema = object({
slug: string()
.min(3, 'Must have at least 3 characters.')
.max(16, 'Cannot be longer than 16 characteres.')
.lowercase()
.trim()
.matches(/^(?![0-9-]+$)(?:[a-z]{2,}-?|[0-9]-?)+(?<!-)$/gm, 'Must start with a letter and can'
+ ' only contain letters, numbers or dashes (no more than one consecutive).')
.required('The slug is required.'),
translations: object({
en: categoryTranslationsSchema,
es: categoryTranslationsSchema
.default(undefined),
de: categoryTranslationsSchema
.default(undefined)
})
})
I think you should look into using a custom locale dictionary. Which allows you to customize the default messages used by Yup, when no message is provided with a validation test. If any message is missing in the custom dictionary the error message will default to Yup's one. It also provided enables multi-language support.
https://github.com/jquense/yup#using-a-custom-locale-dictionary
import { setLocale } from 'yup';
setLocale({
mixed: {
default: 'Não é válido',
},
number: {
min: 'Deve ser maior que ${min}',
},
});
// now use Yup schemas AFTER you defined your custom dictionary
let schema = yup.object().shape({
name: yup.string(),
age: yup.number().min(18),
});
schema.validate({ name: 'jimmy', age: 11 }).catch(function (err) {
err.name; // => 'ValidationError'
err.errors; // => ['Deve ser maior que 18']
});
If you can't get what you're looking for with just that try combining it with yup.lazy which creates a schema that is evaluated at validation/cast time. This can be nested inside your object schema as well. https://github.com/jquense/yup#yuplazyvalue-any--schema-lazy
here is an idea of what you can do:
translations: Yup.lazy(value => {
switch (typeof value) {
case 'object':
return Yup.object(); // schema for object
case 'string':
return Yup.string().min(MIN_DESC_LENGTH).max(_MAX_NAME_LENGTH); // schema for string
default:
return Yup.mixed(); // here you can decide what is the default
}
})
Well, the problem when validating nested object is that they default to empty objects {}, for that reason it passes the validation and enters the inner validation.
The solution is then making all objects .default(undefined) that way we can add more requirements to the object validation itself, in this case making it required.
So the solution was as simple as that:
const categorySchema = object({
slug: string()
.min(3, 'Must have at least 3 characters.')
.max(16, 'Cannot be longer than 16 characteres.')
.lowercase()
.trim()
.matches(/^(?![0-9-]+$)(?:[a-z]{2,}-?|[0-9]-?)+(?<!-)$/gm, 'Must start with a letter and can'
+ ' only contain letters, numbers or dashes (no more than one consecutive).')
.required('The slug is required.'),
translations: object({
en: categoryTranslationsSchema
.default(undefined)
.required('An English translation must be provided.'),
zh: categoryTranslationsSchema
.default(undefined)
}).default(undefined)
.required('Translations must be defined.'),
})
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.')
}),
})
)
});
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;
I am trying to do a validation with Joi, but I came to a scenario that I cannot achieve. I have an object. In some cases, the object will have an id (edit) and on the others it will not (create).
I want to validate that in case "id" is not present, few other fields should be there.
So far my only solution that works is when id is null and not missing at all. Is there a way to match non-existing key or should I change it to null (a bit hacky).
joi.object().keys({
id: joi
.number()
.min(0)
.allow(null),
someOtherField1: joi.when("id", { is: null, then: joi.required() })
});
Thank you in advance!
Undefined is the empty value for Joi. Use "not" instead of "is"
Example: { not: Joi.exist(), then: Joi.required() }
If you're prepared to accept null as an id and would like to perform conditionals on it I'd suggest defaulting the id field to null. This way you can omit it from the payload and still query it using Joi.when().
For example:
Joi.object().keys({
id: Joi.number().min(0).allow(null).default(null),
someOtherField1: Joi.string().when('id', { is: null, then: Joi.required() })
});
Passing both an empty object, {}, and an object with a null id, { "id": null }, will result in:
ValidationError: child "someOtherField1" fails because ["someOtherField1" is required]