Yup dynamic validation chain

Viewed 3223

I'm working with yup validation and trying to build a conditional validation object.

My question is, How do I add the required and match objects to the Yup.string() object without hard coding it. Similar to how you might chain jQuery functions.

Here is an example of what i'm trying to achieve:

if (field.required) {
  valSchema[id] = Yup.string().required(errorText[id].default);
}
if (field.validation) {
  valSchema[id] = Yup.string().matches(re, field.validation[0].message);
}
if (field.otherValidation) {
  valSchema[id] = Yup.string().matches(re, field.validation[1].message);
}

Obviously this isn't going to work because the last condition which is true will overwrite the previous condition.

Then the end result of what would be like this if all the conditions are true.

valSchema[id] = Yup.string()
  .required(errorText[id].default)
  .matches(reExp, field.validation[0].message);
  .matches(reExp1, field.validation[1].message);

any ideas?

Thanks.

2 Answers

To add/chain validation conditionally, you can use Yup's .when method.

This method can add conditional validation based off of the value that is being validated, or via a context object that can be passed as an options argument to both .validate and .isValid calls.

I've created the following simplified example of your code. This solution utilizes Yup's context object passed in during validation. The keys of the context object are referenced using the $ symbol.

const schema = yup.object({
  name: yup.string()
    .when('$required', (required, schema) => (required ? schema.required() : schema))
    .when('$regex1', (regex1, schema) => (regex1 ? schema.matches(/j/) : schema))
    .when('$regex2', (regex2, schema) => (regex2 ? schema.matches(/oe/) : schema))
});

Providing the following object as context would give you the end result in your question where all conditions are true:

{
  required: true,
  regex1: true,
  regex2: true,
}

Putting it all together gives you the following working example:

const yup = require("yup");

const schema = yup.object({
  name: yup.string()
    .when('$required', (required, schema) => (required ? schema.required() : schema))
    .when('$regex1', (regex1, schema) => (regex1 ? schema.matches(/j/) : schema))
    .when('$regex2', (regex2, schema) => (regex2 ? schema.matches(/oe/) : schema))
});

const myObject = {
  name: "joe",
}

const name = await schema.validate(myObject, {
  context: {
    required: true,
    regex1: true,
    regex2: true,
  }
});

console.log(name); // { name: "joe" }

View and test the code live on RunKit here: https://runkit.com/joematune/6138a7db98ff810008ef37ab

I build the entire yup object dynamically. This works for dynamic chaining:

const schema = {};
fields.forEach(f => {
  switch(f.dataType){
    case "string":
      schema[f.id] = Yup.string();
      if (f.required) schema[f.id] = schema[f.id].required();
      if (f.min) schema[f.id] = schema[f.id].min(f.min);
  }
});

return Yup.object().shape(schema);
Related