如何使用Yup验证JSON对象数组的单个元素

问题描述

我有一个JSON对象,其中包含JSON对象数组。我需要的是2个数组元素(车辆),我只需要确保至少一个数组中充满了数据,即两个都不能为空。请参见下面的声明和Yum模式

 const initialValues = {
  applicantID: "",applicantTypeID: "1",firstName: "",lastName: "",email: "[email protected]",address1: "",address2: "",suburb: "",state: "AZ",postcode: "",phone: "",mobile: "",business_unit_school: "",building: "",level: "",room: "",applicationStatusID: "1",**vehicles: [
    { registrationNumber: "",make: "",model: "" },{ registrationNumber: "",],**
  
};

const validationSchema = Yup.object({
  applicantID: Yup.string().required("Employee Number required."),firstName: Yup.string()
    .min(2,"Too Short!")
    .max(30,"Max 30 characters allowed.")
    .required("Firstname required."),lastName: Yup.string()
    .min(2,"Max 30 characters allowed.")
    .required("Lastname required."),email: Yup.string().email("Invalid email format").required("Email required."),address1: Yup.string()
    .min(2,"Too short.")
    .max(255,"Too Long!")
    .required("Address required."),address2: Yup.string().max(255,"Max 255 characters allowed."),suburb: Yup.string()
    .min(2,"Max 30 characters allowed.")
    .required("Suburb required."),state: Yup.string()
    .min(2,"Max 30 characters allowed.")
    .required("State required."),business_unit_school: Yup.string()
    .min(2,"Too Short!")
    .max(100,"Max 100 characters allowed.")
    .required("Business unit required."),**vehicles: Yup.array().of(
    Yup.object().shape({
      registrationNumber: Yup.string().required("required"),})
  ),**
postcode: Yup.string().required("Postcode required."),phone: Yup.number()
    .required("Phone number required")
    .typeError("You must specify a number"),mobile: Yup.number().required("").typeError("You must specify a number"),});

我上面的车辆验证有效,尽管它迫使用户填写不想要的车辆下两个数组项的registrationNumber元素。任何帮助将非常感激。我也在下面尝试过,但不起作用...

let vehicleschema = Yup.object({
  vehicles: Yup.array().of(
    Yup.object({
      registrationNumber: Yup.string().required("required"),make: Yup.string().required("required"),});

const validationSchema = Yup.object({
vehicles: vehicleschema.validateAt("vehicles[0].registrationNumber",initialValues)
}),

我在验证时遇到错误...

TypeError: field.resolve is not a function
(anonymous function)
node_modules/yup/es/object.js:146
  143 | 
  144 | innerOptions.path = makePath(_templateObject(),options.path,prop);
  145 | innerOptions.value = value[prop];
> 146 | field = field.resolve(innerOptions);
      | ^  147 | 
  148 | if (field._strip === true) {
  149 |   isChanged = isChanged || prop in value;
View compiled
ObjectSchema._cast
node_modules/yup/es/object.js:136
  133 | });
  134 | 
  135 | var isChanged = false;
> 136 | props.forEach(function (prop) {
      | ^  137 |   var field = fields[prop];
  138 |   var exists = has(value,prop);
  139 | 

好的,在使用下面的Luis解决方案后,似乎可以验证注册号,尽管我现在遇到了多个错误文本。我正在将Formik与Yup一起使用。Formik代码如下所示...

<Grid
              container
              item
              lg={12}
              md={12}
              xs={12}
              spacing={15}
              name="vehicles"
              style={{ marginBottom: "-4em" }}
            >
              <Box mx={3} my={2} textAlign="center">
                <h2>Vehicle Details</h2>
              </Box>
            </Grid>
            <Grid
              container
              item
              lg={12}
              md={12}
              xs={12}
              spacing={15}
              name="vehicles"
              style={{ marginBottom: "-4em" }}
            ></Grid>
            {initialValues.vehicles.map((vehicle,index) => (
              <Grid
                container
                item
                lg={10}
                md={12}
                xs={12}
                spacing={5}
                justify="space-between"
                className={classes.rowSpacing}
                key={index}
              >
                <Grid item lg={3} md={5} xs={12}>
                  <Field
                    component={TextField}
                    fullWidth={true}
                    label="Registration Number"
                    name={`vehicles[${index}].registrationNumber`}
                  />
                  <FormHelperText error>
                    <ErrorMessage name="vehicles" />
                  </FormHelperText>
                </Grid>

                <Grid item lg={2} md={5} xs={12}>
                  <Field
                    component={TextField}
                    fullWidth={true}
                    label="Make"
                    name={`vehicles[${index}].make`}
                  />
                </Grid>
                <Grid item lg={2} md={5} xs={12}>
                  <Field
                    component={TextField}
                    fullWidth={true}
                    label="Model"
                    name={`vehicles[${index}].model`}
                  />
                </Grid>
                <br />
              </Grid>
            ))}
          </Grid>

请参阅下面的多个错误错误文本“至少需要一个注册号” 重复两次

enter image description here

您好,Luis,更新的解决方案不起作用,可能是由于我在表单中同时使用了Formik-material-ui和material-ui。请参见下文。区分两个TextField元素,我在这里使用muiTextField的别名

import {TextField as muiTextField} from "@material-ui/core";

import { TextField,Select } from "formik-material-ui";

下面是我更新的代码...

<Grid item lg={3} md={5} xs={12}>
                  <Field
                    //component={TextField}
                    fullWidth={true}
                    label="Registration Number"
                    name={`vehicles[${index}].registrationNumber`}
                    render={() => (
                      <muiTextField
                         error={Boolean(errors.vehicles)}
                         helperText= {
                            errors.vehicles && getVehiclesErrors(errors.vehicles)
                         }
                      />
                   )}
                  />
                  
                </Grid>

formik-material-ui只是普通的material-ui元素的包装。我很好奇为什么在getVehicleErrors()函数中有“电子邮件”引用。那是错字吗?在更新代码(如上所述)之后,此处muiTextField指的是Material-ui的TextField,现在我在表单上看不到任何车辆注册字段。.请参见下文

enter image description here

已通过以下更改设法解决了该问题。...

    const getVehiclesErrors = (errors) => {
    return Array.isArray(errors)
      ? errors.filter((registrationNumber,i,arr) => arr.indexOf(registrationNumber) === i)
      : errors;
  }; 



<Grid item lg={3} md={5} xs={12}>
                      <Field
                        component={TextField}
                        fullWidth={true}
                        label="Registration Number"
                        name={`vehicles[${index}].registrationNumber`}
                      />
                      {errors.vehicles && touched.vehicles ? (
                        <div style={{ color: "red" }}>
                          {getVehiclesErrors(errors.vehicles)}
                        </div>
                      ) : null}
                    </Grid>

enter image description here

解决方法

您可以这样操作:

*我已经完成了一个示例,其中至少应填写数组中的一个registrationNumber 适用于您的车辆架构:

vehicles: Yup.array(
    Yup.object({
      registrationNumber: Yup.string(),make: Yup.string().required("make Required"),}).test(
      "registrationNumber test",// The error message that should appears if test failed
      "at least one registrationNumber should be filled",// Function that does the custom validation to this array
      validateAgainstPrevious
    )
  )

下面的功能只是一个例子。您可以在此处执行自己的逻辑。

function validateAgainstPrevious() {
  // In this case,parent is the entire array
  const { parent } = this;
  
  // filtered array vechicles that doens't have registrationNumber
  const filteredArray = parent.filter((e) => !e.registrationNumber);
  
  // If length of vehicles that doesn't have registrationNumber is equals to vehicles  array length then return false;
  if (filteredArray.length === parent.length) return false;

  return true;
}

已更新

对于多个错误文本问题,您可以采取一种解决方法:

相反,您将TextField组件传递给Field,就像这样:

<Field
   component={TextField}
   fullWidth={true}
   label="Registration Number"
   name={`vehicles[${index}].registrationNumber`}
/>

您可以执行以下操作:

<Field
   // component={TextField}
   fullWidth={true}
   label="Registration Number"
   name={`vehicles[${index}].registrationNumber`}
   render={() => (
      <TextField
         error={Boolean(errors.vehicles)}
         helperText= {
            errors.vehicles && getVehiclesErrors(errors.vehicles)
         }
      />
   )}
/>

并创建此功能:

const getVehiclesErrors = (errors) => {
  return Array.isArray(errors)
    ? errors.filter((email,i,arr) => arr.indexOf(email) === i)
    : errors;
};
,

取而代之的是 Yup.object({}) 使用 Yup.object().shape({ ... 所有属性都在这里})