如何使用 Yup 验证 JSON 对象数组的单个元素
How to validate individual element of an array of JSON objects using Yup
我有一个 JSON 对象,其中包含一个 JSON 对象数组。我需要的是 2 个数组元素(车辆),我只需要确保至少有一个填充了数据,即两者都不能为空。请参阅下面的声明和我的 Yum 架构
const initialValues = {
applicantID: "",
applicantTypeID: "1",
firstName: "",
lastName: "",
email: "user@abc.co",
address1: "",
address2: "",
suburb: "",
state: "AZ",
postcode: "",
phone: "",
mobile: "",
business_unit_school: "",
building: "",
level: "",
room: "",
applicationStatusID: "1",
**vehicles: [
{ registrationNumber: "", make: "", model: "" },
{ registrationNumber: "", make: "", model: "" },
],**
};
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, "Too Short!")
.max(30, "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, "Too Short!")
.max(30, "Max 30 characters allowed.")
.required("Suburb required."),
state: Yup.string()
.min(2, "Too Short!")
.max(30, "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>
查看下面的多个错误..错误文本“至少需要一个注册号ui红色” 重复两次
嗨,Luis,更新后的解决方案不起作用,这可能是因为我同时使用了 Formik-material-ui 和 material-ui 在我的表单中..见下文..为了在两个 TextField 元素之间区分uish,我在这里使用 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() 函数中有 'email' 引用。那是打错了吗?在我更新我的代码(按照上面)后,这里 muiTextField 指的是 material-ui 的 TextField,现在我在我的表格上看不到任何车辆登记字段..见下文
设法通过以下更改解决问题....
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>
你可以这样做:
*我做了一个示例,其中至少应填充数组中的一个 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({ ... 所有属性都在这里})
我有一个 JSON 对象,其中包含一个 JSON 对象数组。我需要的是 2 个数组元素(车辆),我只需要确保至少有一个填充了数据,即两者都不能为空。请参阅下面的声明和我的 Yum 架构
const initialValues = {
applicantID: "",
applicantTypeID: "1",
firstName: "",
lastName: "",
email: "user@abc.co",
address1: "",
address2: "",
suburb: "",
state: "AZ",
postcode: "",
phone: "",
mobile: "",
business_unit_school: "",
building: "",
level: "",
room: "",
applicationStatusID: "1",
**vehicles: [
{ registrationNumber: "", make: "", model: "" },
{ registrationNumber: "", make: "", model: "" },
],**
};
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, "Too Short!")
.max(30, "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, "Too Short!")
.max(30, "Max 30 characters allowed.")
.required("Suburb required."),
state: Yup.string()
.min(2, "Too Short!")
.max(30, "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>
查看下面的多个错误..错误文本“至少需要一个注册号ui红色” 重复两次
嗨,Luis,更新后的解决方案不起作用,这可能是因为我同时使用了 Formik-material-ui 和 material-ui 在我的表单中..见下文..为了在两个 TextField 元素之间区分uish,我在这里使用 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() 函数中有 'email' 引用。那是打错了吗?在我更新我的代码(按照上面)后,这里 muiTextField 指的是 material-ui 的 TextField,现在我在我的表格上看不到任何车辆登记字段..见下文
设法通过以下更改解决问题....
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>
你可以这样做:
*我做了一个示例,其中至少应填充数组中的一个 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({ ... 所有属性都在这里})