I have a JSON object which contains an array of JSON objects. My need is out of 2 array elements (vehicles), I just need to ensure that at least one is filled with data i.e. both can't be empty.. see my declaration below and my Yum schema
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: "", 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"),
});
My above vehicles validation works though it forces user to fill in registrationNumber element of both array items under vehicle which is not I want. Any help would be much appreciated. I also tried below and it doesn't work ...
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)
}),
I get below error on validation ...
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 |
ok, after using Luis's solution below, it seems to validate registration numbers though I am now ended up with multiple error text. I am using Formik with Yup.. the Formik code is as per below ...
<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>
see multiple error below ..error text "at least one registration number is required" getting repeated twice
Hi Luis that updated solution didn't work and it could be due to the fact that I use both Formik-material-ui and material-ui in my forms..see below.. to distinguish between two TextField elements, I am using alias of muiTextField here
import {TextField as muiTextField} from "@material-ui/core";
import { TextField, Select } from "formik-material-ui";
below is my updated code ...
<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>
The formik-material-ui is just a wrapper around common material-ui elements. I am curious as to why you have 'email' reference in the getVehicleErrors() function. is that a typo? After I updated my code (as per above) here muiTextField refers to the TextField of material-ui, now I see no vehicle registration fields on my form..see below
Managed to fix the issue with below changes....
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>