jquense / yup

Dead simple Object schema validation
MIT License
22.93k stars 934 forks source link

How to create yup schema for dynamic array of different objects #2191

Closed malibeg closed 7 months ago

malibeg commented 7 months ago

How to create yup schema for array of objects of different type.

E.g. if I receive from backend questionnaire json where questions can be text input, choice, or numeric input. Every question object has unique id and type:

{
   "questionnaire":{
      "name":"set name",
      "description":"description",
      "questions":[
         {
            "id":"TextQuestion1",
            "text":"First Name",
            "type": "text",
            "answer":""
         },
         {
            "id":"MultilineQuestion1",
            "text":"Last Name",
            "type": "multiline",
            "answer":""
         },
         {
            "id":"NumericQuestion3",
            "type": "number",
            "text":"your Age",
            "NumValue":5
         },
         {
            "id":"ChoiceQuestion4",
            "type": "choice",
            "availableColours":[
               "red",
               "green",
               "blue"
            ],
            "favColours":"green"
         }
      ]
   }
}
jquense commented 7 months ago

You can use lazy to return different schema depending on the value type

malibeg commented 7 months ago

Thanks this works:

import React from "react";
import * as yup from "yup";

import "./styles.css";

model = {
  questionnaire: {
    name: "set name",
    description: "description",
    questions: [
      {
        id: "TextQuestion1",
        text: "First Name",
        type: "text",
        answer: "bla",
      },
      {
        id: "MultilineQuestion1",
        text: "Last Name",
        type: "multiline",
        answer: "bla",
      },
      {
        id: "NumericQuestion3",
        type: "number",
        text: "your Age",
        NumValue: 5,
      },
      {
        id: "ChoiceQuestion4",
        type: "choice",
        availableColours: ["red", "green", "blue"],
        favColours: "green",
      },
    ],
  },
};

import * as Yup from "yup";

const schema = Yup.object().shape({
  questionnaire: Yup.object().shape({
    name: Yup.string().required("Name is required"),
    description: Yup.string().required("Description is required"),
    questions: Yup.array()
      .of(
        Yup.lazy((item) => {
          switch (item.type) {
            case "text":
              return Yup.object().shape({
                id: Yup.string().required("ID is required"),
                text: Yup.string().required("Text is required"),
                type: Yup.string().required("Type is required"),
                answer: Yup.string().required("Answer is required"),
              });
            case "multiline":
              return Yup.object().shape({
                id: Yup.string().required("ID is required"),
                text: Yup.string().required("Text is required"),
                type: Yup.string().required("Type is required"),
                answer: Yup.string().required("Answer is required"),
              });
            case "number":
              return Yup.object().shape({
                id: Yup.string().required("ID is required"),
                text: Yup.string().required("Text is required"),
                type: Yup.string().required("Type is required"),
                NumValue: Yup.number().required("NumValue is required"),
              });
            case "choice":
              return Yup.object().shape({
                id: Yup.string().required("ID is required"),
                type: Yup.string().required("Type is required"),
                availableColours: Yup.array()
                  .of(Yup.string().required())
                  .required("Available colours are required"),
                favColours: Yup.string().required(
                  "Favourite colour is required"
                ),
              });
            default:
              return Yup.object();
          }
        })
      )
      .required("Questions are required"),
  }),
});

export default function App() {
  return (
    <div className="App">
      <p>{JSON.stringify(schema.validateSync(model, { returnError: true }))}</p>
      {/* <p>{JSON.stringify(workflow)}</p> */}
      {/* <p>{schema.isValidSync(workflow) ? "Valid" : "Invalid"}</p> */}
    </div>
  );
}