It is possible to validate in this manner using the context option passed in when calling .validate.
A simple but illustrative example of the context option - passing a require_name key (with truthy value) to the .validate call will change the schema to be .required():
const NameSchema = yup.string().when(
'$require_name',
(value, schema) => value ? schema.required() : schema
);
const name = await NameSchema.validate('joe', {
context: {
require_name: true,
}
});
If we can pass in the required fields to the schema during validation, we can use the following schema for your validation case:
const AllOrNoneSchema = yup.object({
name_german: yup.string()
.when('$name_german', (value, schema) => value ? schema.required() : schema),
description_german: yup.string()
.when('$description_german', (value, schema) => value ? schema.required() : schema),
date_german: yup.string()
.when('$date_german', (value, schema) => value ? schema.required() : schema),
// ...other_languages
});
In your case, we must pass in a required context key for each language if one or more of the fields is filled out. i.e. If one German field has been filled out, we want to pass an object with all ${something}_german keys whose values are true:
{
name_german: true,
description_german: true,
...
}
To do this, we can create a helper function that takes your form object and returns a record with boolean values as in the example above:
const createValidationContext = (form) => {
const entries = Object.entries(form);
const requiredLanguages = entries.reduce((acc, [key, value]) => {
return !value.length ? acc : [...acc, key.split('_')[1]];
}, []);
return Object.fromEntries(
entries.map(([key, value]) => [key, requiredLanguages.includes(key.split('_')[1])])
)
};
Putting it all together, we have the following working solution:
const yup = require("yup");
const AllOrNoneSchema = yup.object({
name_german: yup.string()
.when('$name_german', (value, schema) => value ? schema.required() : schema),
description_german: yup.string()
.when('$description_german', (value, schema) => value ? schema.required() : schema),
date_german: yup.string()
.when('$date_german', (value, schema) => value ? schema.required() : schema),
});
const createValidationContext = (form) => {
const entries = Object.entries(form);
const requiredLanguages = entries.reduce((acc, [key, value]) => {
return !value.length ? acc : [...acc, key.split('_')[1]];
}, []);
return Object.fromEntries(
entries.map(([key, value]) => [key, requiredLanguages.includes(key.split('_')[1])])
)
};
const form = {
description_german: "nein",
date_german: "juli",
name_german: "nena",
}
const formContext = createValidationContext(form);
console.log(formContext); // { description_german: true, date_german: true, name_german: true }
const validatedForm = await AllOrNoneSchema.validate(form, { context: formContext });
console.log(validatedForm); // { description_german: "nein", date_german: "juli", name_german: "nena" }
A live example of this solution, with multiple languages, can be seen an tested on RunKit, here: https://runkit.com/joematune/6138ca762dc6340009691d8a
For more information on Yup's context api, check out their docs here: https://github.com/jquense/yup#mixedwhenkeys-string--arraystring-builder-object--value-schema-schema-schema