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.'),
})
Related
So, I have this list of some brandnames:
const blackListedDomains = [
'facebook',
'google',
'amazon',
'netflix',
'hotmail',
'microsoft',
'gmail',
'yahoo',
]
which I need to prevent users to enter in the email field, what Schema could be built using Yup Package.
For example, I am trying this way:
const DomainSchema = Yup.object().shape({
subdomain: Yup.string()
.required('Required')
.lowercase()
.notOneOf(
blackListedDomains,
'Cannot use trademarked names in domain!',
),
})
But, I don't know how to get it from the email string, for example, if user inputs xyz#google.xyz, then how to extract google from the email string and then iterate it over the domain names list to find if it exists there and then show an error message ?
You can use test function of yup library to create a custom validation rule like this:
const DomainSchema = Yup.object().shape({
email: Yup.string()
.required("Required")
.lowercase()
.email("Not a valid mail")
.test(
"is-not-blacklist",
"Cannot use trademarked names in domain!",
(value) => {
if (value) {
const currentDomain = value.substring(
value.indexOf("#") + 1,
value.indexOf(".")
);
return !blackListedDomains.includes(currentDomain);
}
}
)
});
You can take a look at this sandbox for a live working example of this solution.
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 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
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();
I have written a schema but it does not seem to be validating as I was expecting. I'm assuming there is something wrong with my schema syntax but cannot figure it out. I expect not to see error messages for title or target until fundraiser is complete since they are only required if fundraiser is completed. I've tried many combinations but none of them are working as expected, these two are the closest I've come to what I need.
Schema attempt one: shows 4 error messages, 3 required errors and 1 error saying data should match "then" schema.
const schema = {
required: ['fundraiser'],
if: {
properties: {
fundraiser: { type: 'string' },
},
},
then: {
required: ['title', 'target'],
},
errorMessage: {
required: {
fundraiser: 'Please select an option',
title: 'Please enter a title',
target: 'Please enter a target',
},
},
};
Schema attempt two: shows 2 error messages, 1 required error and 1 error saying data should match "then" schema which is correct but then when I complete fundraiser valid becomes true which is when I expect to then see required errors for title and target. Also no errors have my defined custom error messages.
const scema = {
if: {
properties: { fundraiser: { minLength: 2 } },
then: { required: ['title', 'target'] },
},
then: { required: ['fundraiser'] },
errorMessage: {
required: {
fundraiser: 'Please select an option',
title: 'Please enter a title',
target: 'Please enter a target',
},
},
};
I am pretty sure that I am doing something wrong with my schema but it is not clear from the documentation how to use if/then in combination with custom error messages using ajv-errors. Any help would be greatly appreciated! Thanks!
The problem with the first schema is that subschema inside “if” is valid, unless fundraiser property is present and not a string. It would probably work as you expect if you add type: 'object' to the root schema and move required inside “if” subschema.
The problem with the second subschema is that the first “then” that has no “if” in the same schema object is ignored (unless you are using ajv-keywords that implemented if/then/else somewhat differently from how it is defined in draft-07 of JSON Schema spec) and the subschema inside “if is valid even if fundraiser property is absent and the second “then” can only pass if fundraiser is present.