I'm trying to build a form with Formik but I'm unable to do the type of conditional validation that I need for my schemas. I have a Formik form where I'm trying to fill out an object called profile. The object looks something like this, this is what I have in my Formik initialValues:
const profile = {
profile_first_name: undefined,
profile_last_name: undefined,
profile_time_zone: undefined,
profile_company_name: undefined,
profile_work_histories: [
profile_work_histories
],
profile_educations: [
profile_educations
]
};
Here is how my schema looks like:
export const ClientCreateAccountSchema = Yup.object().shape({
profile: Yup.object().shape({
profile_first_name: Yup.string().required('Cannot be empty'),
profile_last_name: Yup.string().required('Cannot be empty'),
profile_time_zone: Yup.string().required('Cannot be empty').length(3, 'Must be 3 characters (ex.PST)'),
profile_company_name: Yup.string().required('Cannot be empty'),
profile_work_histories: profileWorkHistory,
profile_educations: profileEducationSchema
})
});
export const profileEducationSchema = Yup.array().of(Yup.object().shape({
profile_education_school_name: Yup.string().required(),
profile_education_degree_type: Yup.string().required(),
profile_education_degree_major: Yup.string().required(),
profile_education_school_country: Yup.string().required(),
profile_education_school_province: Yup.string().required(),
profile_education_start_date: Yup.string().required(),
profile_education_end_date: Yup.string().matches(/^((0[1-9])|(1[0-2]))-[0-9]{4}$/, 'Must be in MM-YYYY format')
}));
export const profileWorkHistory = Yup.array().of(Yup.object().shape({
profile_work_history_company_name: Yup.string().required(),
profile_work_history_employment_url: Yup.string().url(),
profile_work_history_number_of_attorneys: Yup.number('Must be a number').required(),
profile_work_history_company_country: Yup.string().required(),
profile_work_history_company_province: Yup.string().required(),
profile_work_history_company_city: Yup.string().required(),
profile_work_history_position: Yup.string().required(),
profile_work_history_employment_start_date: Yup.string().required(),
profile_work_history_employment_end_date: Yup.string().required(),
profile_work_history_is_current: Yup.bool().required()
}));
Keep in mind that I also have 4 other subschemas at that level, but I removed them for simplicity. The other subschemas are called profileBarAssociationsSchema, profileJurisdictionSchema, profileLanguagesSchema, profileIndustrySchema. They all have up to 6 fields that need to be filed out.
So far this is what I've tried:
const requiredIf = (parent, dependent) => {
let keys = parent && Object.keys(parent);
let allValuesExist = keys.filter(field => parent && parent[field]).length !== 0;
return !allValuesExist || parent && parent[dependent] && parent[dependent] !== '';
};
export const profileEducationSchema = Yup.array().of(Yup.object().shape({
profile_education_school_name: Yup.string()
.test('required-If', 'Cannot be empty', function () {
return requiredIf(this.parent, 'profile_education_school_name');
}),
profile_education_degree_type: Yup.string()
.test('required-If', 'Cannot be empty', function () {
return requiredIf(this.parent, 'profile_education_degree_type');
}),
profile_education_degree_major: Yup.string()
.test('required-If', 'Cannot be empty', function () {
return requiredIf(this.parent, 'profile_education_degree_major');
}),
profile_education_school_country: Yup.string()
.test('required-If', 'Cannot be empty', function () {
return requiredIf(this.parent, 'profile_education_school_country');
}),
profile_education_school_province: Yup.string()
.test('required-If', 'Cannot be empty', function () {
return requiredIf(this.parent, 'profile_education_school_province');
}),
profile_education_start_date: Yup.string()
.test('matches-If','Must be in MM-YYYY format', function (value) {
return value == null || value === '' ? true : /^((0[1-9])|(1[0-2]))-[0-9]{4}$/.test(value);
})
.test('required-If', 'Cannot be empty', function () {
return requiredIf(this.parent, 'profile_education_start_date');
}),
profile_education_end_date: Yup.string().matches(/^((0[1-9])|(1[0-2]))-[0-9]{4}$/, 'Must be in MM-YYYY format')
.test('required-If', 'Cannot be empty', function () {
return requiredIf(this.parent, 'profile_education_end_date');
})
}));
But I get an error everything I tried to add a new field to the FieldArray and start filling out the second item instead of the first one.
Another thing I've tried was to use Yup.when but I have so many fields that it is hard to keep track of all the conditional validation combinations, that makes it almost impossible .
My question is how can I configure my ClientCreateAccountSchema so that the sub schemas are validated separately from each other? In other words, how can I my schema only validate the respective subchema when touched and not activate the other subschema? Is this something I can do with Yup? or is this something with Formik?
What other suggestions do you guys have?
A few options:
jsx
<Formik validationSchema={values => {
let schemaShape = {}
if (values.thing) {
return Yup.object().shape({
thing: Yup.string().required()
profileOtherThing: Yup.string().required()
})
}
// otherwise do something else
return Yup.object().shape({/* ... */})
}}/>
validate function that uses Yup and perhaps Yup.schema.validateAt() + some biz logic. You will likely want to utilize this helper method too https://github.com/jaredpalmer/formik/blob/master/src/Formik.tsx#L890@jaredpalmer On another note I think validationSchema = { values => { does not pass through the values . validate = { values => { will do though.
Is it possible to separate the Form into smaller forms and submit them all at once ?
Most helpful comment
@jaredpalmer On another note I think
validationSchema = { values => {does not pass through thevalues.validate = { values => {will do though.