zod validation for an array of objects

I am trying to add validation for a form that has a checkbox selection with a number input next to each checkbox. A user selects a profession checkbox and then enters the number of years of experience they have in the input next to it. The array looks like this (experience has a default of 1):

const fieldsOfEng = [ { id: "ELECTRICAL", name: "Electrical", experience: 1, }, { id: "MECHANICAL", name: "Mechanical", experience: 1, }
]

This is how the schema would look if I was just verifying that the user selected one of the options in my professions array

export const userInfoSchema = z.object({ professions: z .string() .array() .refine((val) => val.some(profession => fieldsOfEng .map((field) => field.name) .includes(profession)))
})

with the input being registered via react-hook-form like:

{fieldsOfEng.map((field) => { return ( <input {...register("professions")} value={field.name} type="checkbox" /> )}

--------------------WHAT I WANT:

I'd like to add an 'experience' field to my schema so I it would look something like (but this isn't correct):

 professions: z .array( z.object({ name: z.string(), experience: z.number(), }) ) .refine(({name}) => name.some(({ profession }) => fieldsOfEng.map((field) => field.name).includes(profession) ) ) .refine(({ experience }) => { experience.some((exp) => exp > 1); }),

And I think my form would look something like:

{fieldsOfEng.map((field) => { return ( <input {...register("professions.name")} value={field.name} type="checkbox" /> <input {...register("professions.experience")} value={field.experience} type="number" /> )}

I can always experiment with the form but my main concern is the schema.

3 Answers

If you want to validate an array of objects from this Schema

const fieldsOfEng = [ { id: "ELECTRICAL", name: "Electrical", experience: 1, }, { id: "MECHANICAL", name: "Mechanical", experience: undefined, },
];

I'd do it like this

const userInfoSchema = z.object({ id: z.string(), name: z.string(), experience: z.number().optional()
})
// Now add this object into an array
const usersInfoSchema = z.array(userInfoSchema)

This answer is an update on my progress so far. I closer to getting my schema validation working correctly but for some reason it is not submitting. I could solve this faster if I could get react-hook form's errors to output correctly but since I'm using template literals when I'm registering the inputs, the error is just outputting nothing. I DO know there is an error though because my truthy error outputs the "professions:" string that I added in the p-tag.

Data for checkboxes in form (this is the data I'm validating via zod):

const fieldsOfEng = [ { id: "ELECTRICAL", name: "Electrical", experience: undefined, }, { id: "MECHANICAL", name: "Mechanical", experience: undefined, },

Schema for validation: I'm checking that the name they select is included in my fieldsOfEng array and making sure the experience is greater than 1.

const userInfoSchema = object({
professions: z .object({ name: z .string() .refine((name) => fieldsOfEng.map((field) => field.name).includes(name) ), experience: z.number().refine((experience) => experience > 1), }) .array(),
});

React hook form:

type userInfoType = z.infer<typeof userInfoSchema>; const { register, watch, handleSubmit, formState: { errors, isSubmitting }, } = useForm<userInfoType>({ resolver: zodResolver(userInfoSchema), });

Form: Notice I am using template literals for registering each input. It took me forever to figure this out. So far this is the only way I've been able to get the data to output correctly (via react-hook-form's 'watch' method).

<fieldset> <legend>Practicing fields of engineering</legend> {fieldsOfEng.map((field, i) => { return ( <div key={field.id}> <div> <input {...register(`professions.${i}.name`)} <--REGISTERED HERE value={field.name} type="checkbox" /> <div> <input {...register(`professions.${i}.experience`)} <--REGISTERED HERE type="number" value={field.experience} placeholder="Yrs Experience" /> </div> </div> <div> {errors.professions && ( <p> professions: {errors.professions.message} </p> )} </div> </div> ); })} </fieldset>

I think zod wont be printing out errors if the array does not have default value upon submitting. Set the default values of professions inside your useForm:

const form = useForm<z.infer<typeof userInfoSchema>>({ resolver: zodResolver(formSchema), defaultValues: { professions: [], }, mode: "onChange", }); 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like