Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create dynamic Yup validation schema from JSON

I'm trying to use Yup along with Formik in my react form. The form fields are going to be dynamic so as their validations.

export const formData = [
  {
    id: "name",
    label: "Full name",
    placeholder: "Enter full name",
    type: "text",
    required: true,
    value: "User name",
    values: [],
    validations: [
      {
        type: "minLength",
        value: "5",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "maxLength",
        value: "10",
        error_message: "name should be atleast 5 char long"
      }
    ]
  },
  {
    id: "email",
    label: "Email",
    placeholder: "Email",
    type: "text",
    required: true,
    value: "email",
    values: [],
    validations: [
      {
        type: "minLength",
        value: "5",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "maxLength",
        value: "10",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "email",
        error_message: "Valid email"
      }
    ]
  },
  {
    id: "phoneNumber",
    label: "phone number",
    type: "text",
    required: true,
    value: "7878787878",
    values: [],
    validations: [
      {
        type: "minLength",
        value: "5",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "maxLength",
        value: "10",
        error_message: "name should be atleast 5 char long"
      },
      {
        type: "required",
        error_message: "phone number is required"
      }
    ]
  },
  {
    id: "total",
    label: "Total People in Family",
    placeholder: "family members count",
    type: "text",
    required: false,
    value: "1",
    values: [],
    validations: [
      {
        type: "minLength",
        value: "1",
        error_message: "there should be atleast 1 family member"
      },
      {
        type: "maxLength",
        value: "5",
        error_message: "max family members can be 5"
      }
    ]
  }
]

 let validateSchema = yup.object().shape({
     name: yup.string().required("name is required"),
     email: yup.string().email(),
     phoneNumber: yup.number().min(10, "minium 10 numbers"),
     total: yup
       .number()
       .min(1, "minium 1 member")
       .max(5, "max 5 member")
       .required("member is required")    });
  • What I'm currently doing is iterating over the above array and calling the corresponding React form components.
  • Validation is currently handled by Yup. I'm aware that you can create static Yup validation schema like above `validateSchema' variable.
  • Now I want to create this validation schema depending upon the values in the formData.validation array. I tried some of the ways in this codesandbox but still unable to figure it out. Also, I looked into the Yup.lazy but it seems highly confusing to me.

Any help will be appreciated :)

Codesandbox

like image 236
vijayscode Avatar asked Jun 23 '19 15:06

vijayscode


2 Answers

In case someone is trying to create yupschema on the fly. With some help, I was able to do it.

import * as yup from "yup";

export function createYupSchema(schema, config) {
  const { id, validationType, validations = [] } = config;
  if (!yup[validationType]) {
    return schema;
  }
  let validator = yup[validationType]();
  validations.forEach(validation => {
    const { params, type } = validation;
    if (!validator[type]) {
      return;
    }
    console.log(type, params);
    validator = validator[type](...params);
  });
  schema[id] = validator;
  return schema;
}

Codesandbox

like image 108
vijayscode Avatar answered Nov 18 '22 17:11

vijayscode


If you're looking for more functionality, consider schema-to-yup.

But @vijayscode's answer is super handy. Here's an attempt to extend their example to include when conditions:

import * as yup from "yup";

function createYupSchema(schema, config) {
  const { id, validationType, validations = [] } = config;
  if (!yup[validationType]) {
    return schema;
  }
  let validator = yup[validationType]();
  validations.forEach((validation) => {
    const { params, type } = validation;
    if (!validator[type]) {
      return;
    }
    if (type === "when") {
      const { is, then, otherwise } = params[1];
      let whenParams = {};
      whenParams.is = is;
      whenParams.then = (schema) => schema[then[0].type](...then[0].params);

      if (otherwise) {
        whenParams.otherwise = (schema) =>
          schema[otherwise[0].type](...otherwise[0].params);
      }

      validator = validator["when"](params[0], whenParams);
    } else {
      validator = validator[type](...params);
    }
  });
  schema[id] = validator;
  return schema;
}

And define your config like so:

const myConfig = [
  {
    id: "isBig",
    validationType: "boolean",
  },
  {
    id: "count",
    validationType: "number",
    validations: [
      {
        type: "when",
        params: [
          "isBig",
          {
            is: true,
            then: [
              {
                type: "min",
                params: [5],
              },
            ],
            otherwise: [
              {
                type: "min",
                params: [0],
              },
            ],
          },
        ],
      },
    ],
  },
];

const schema = myConfig.reduce(createYupSchema, {});
const validateSchema = yup.object().shape(schema);
like image 1
spatialaustin Avatar answered Nov 18 '22 18:11

spatialaustin