Scenario:
If there are 3 participants, all 3 participants will have the same set of questions but each of those questions have a list of "applied tickets" which will decide whether or not the question gets shown to a specific participant.
TLDR;
I need to know
- if it is possible to turn off validations for specific fields at runtime using
yup(as far as I know this should not be possible since all field validations are provided at the start) - what other alternatives do I have to get this validation done ? I'm okay with using Field level validations but I still want to be able to utilize
yupbecause of all the in-built validation checks
Details:
I have a form which requires me to conditionally render specific fields in Formik but also remove the validation from it if the field doesn't exist.
The specific issue with this form, is that it contains multiple "sub-forms" handled via Formiks FieldArray. The structure of which looks like this.
The fields inside these sub-forms can be turned on and off but that doesn't apply for the validation schema.
So far, I've tried using the yup.when() function to conditionally render it and it does what's intended but it removes it for all of the elements in the form. The snippet is attached here,
/**
* Given a list of applied tickets, turn off the validation whenever the "id" property
* does not exist in the appliedTickets array
*/
if (field.appliedTickets.length > 0) {
schema = schema.when("id", {
is: (value: any) => field.appliedTickets.every((t) => t !== value),
then: (s) => s.notRequired(),
otherwise: (s) => s,
});
}
Update 1 : Added Schema's
Main Schema
const personalDetailsSchema = useMemo(() => {
const ticketSchema = {
id: string().strip(),
firstName: string()
.trim()
.required(getErrorMessage("firstName.label", { required: true })),
lastName: string()
.trim()
.required(getErrorMessage("lastName.label", { required: true })),
gender: mixed<Gender>()
.required(getErrorMessage("gender.label", { required: true }))
.defined()
.default(Gender.male),
email: string()
.email("Must be a valid email")
.required(getErrorMessage("email.label", { required: true })),
dateOfBirth: date()
.required(getErrorMessage("dateOfBirth.label", { required: true }))
.typeError("A valid date is required")
.nullable()
.default(null),
nationality: string()
.oneOf<Country["abbr"]>(
allCountries.map((c) => c.abbr),
"An available nationality is required"
)
.required(getErrorMessage("nationality.label", { required: true }))
.defined(),
extraInfo: customQuestionSchema
? object({ id: string().strip(), ...customQuestionSchema })
: mixed().notRequired(),
};
const personalDetailsSchema = object({
tickets: array(object(ticketSchema)),
});
return personalDetailsSchema;
}, [getErrorMessage, customQuestionSchema]);
Custom Question Schema
Each custom question here has a shape containing isPredefinedType. Based on this, there are two functions that dynamically generate the rest of the schema based on data provided by the custom question.
const customQuestionSchema: Nullable<Record<string, AnySchema>> =
useMemo(() => {
if (!customQuestions) {
return;
}
const jointSchema = customQuestions?.reduce((previous, current) => {
if (current.isPreDefinedType) {
const presetQuestionSchema = getPredefinedSchemaFor(current);
if (presetQuestionSchema) {
return Object.assign(previous, {
[presetQuestionSchema.name]: presetQuestionSchema.objectSchema,
});
}
}
const { name, objectSchema } = getCustomSchemaFor(current);
return Object.assign(previous, {
[name]: objectSchema,
});
}, {});
return jointSchema;
}, [customQuestions, getCustomSchemaFor, getPredefinedSchemaFor]);
const personalDetailsSchema = object({
tickets: array(object(ticketSchema)),
})
