Reputation: 243
I'm building a form for a client and one of the questions want the user to select a checkbox (or multiple checkboxes). I haven't fully grasped zod's schema so I'm a little stuck on how to implement this. I tried changing around the schema a couple times, I even mapped through an array of the items I wanted to be checked and rendered them as checkboxes but I was unsuccessful.
I'm also trying to assign string to the checkboxes (i'm not using them for true and false questions).
Here's my form field:
<FormField
name="reservationMethod"
control={form.control}
render={({ field }) => (
<FormItem className="flex flex-col gap-4">
<FormLabel className="text-base md:text-lg text-black font-extralight">
Which of the following did you use to make your
reservation?
</FormLabel>
<FormControl>
<div className="flex gap-4">
<div className="flex items-center gap-1">
<Checkbox
id="tourOperator"
value="Tour Operator"
checked={field.value.includes("Tour Operator")}
onCheckedChange={() =>
handleCheckboxChange("Tour Operator")
}
/>
<Label htmlFor="tourOperator">Tour Operator</Label>
</div>
<div className="flex items-center gap-1">
<Checkbox
id="internet"
value="Internet"
checked={field.value.includes("Internet")}
onCheckedChange={() =>
handleCheckboxChange("Internet")
}
/>
<Label htmlFor="internet">Internet</Label>
</div>
<div className="flex items-center gap-1">
<Checkbox
id="directlyWithResort"
value="Directly with the resort"
checked={field.value.includes(
"Directly with the resort"
)}
onCheckedChange={() =>
handleCheckboxChange("Directly with the resort")
}
/>
<Label htmlFor="directlyWithResort">
Directly with the resort
</Label>
</div>
<div className="flex items-center gap-1">
<Checkbox
id="travelAgent"
value="Travel Agent"
checked={field.value.includes("Travel Agent")}
onCheckedChange={() =>
handleCheckboxChange("Travel Agent")
}
/>
<Label htmlFor="travelAgent">Travel Agent</Label>
</div>
<div className="flex justify-center items-center ml-10">
<Label>Name of agency: </Label>
<Input type="text" {...field} />
</div>
</div>
</FormControl>
</FormItem>
)}
/>
My schema for that specific field: reservationMethod: z.string().array(),
Upvotes: 2
Views: 10042
Reputation: 11
I tried to do like this and thats worked for me
use checkbox lists as object key value pairs
import {
Form,
FormControl,
FormField,
FormLabel,
FormMessage,
FormItem,
FormDescription,
} from "@/components/ui/form";
import { Checkbox } from "@/components/ui/checkbox";
type UnexpectedObject = {
[key: string]: any;
};
type Props = {
form: any;
fieldControlName: any;
fieldLabel: any;
checkboxItems: UnexpectedObject;
};
const MultipleCheckboxWithLabel = ({
form,
fieldControlName,
fieldLabel,
checkboxItems,
}: Props) => {
return (
<FormField
control={form.control}
name={fieldControlName}
render={() => (
<FormItem className="">
<div className="mb-4">
<FormLabel className="text-base">{fieldLabel}</FormLabel>
</div>
{Object.entries(checkboxItems).map(([key, value], _idx) => (
<FormField
key={_idx}
control={form.control}
name={fieldControlName}
render={({ field }) => {
return (
<FormItem
key={key}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={field.value?.includes(key)}
onCheckedChange={(checked) => {
return checked
? field.onChange([...field.value, key])
: field.onChange(
field.value?.filter(
(value: any) => value !== key
)
);
}}
/>
</FormControl>
<FormLabel className="font-normal">{value}</FormLabel>
</FormItem>
);
}}
/>
))}
<FormMessage />
</FormItem>
)}
/>
);
};
export default MultipleCheckboxWithLabel;
Upvotes: 0
Reputation: 1075
Another way of using chadcn ui component with react-hook-form that I find easy is to use control
.
const schema = z.object({
title: z.string().min(5, { message: 'Must be 5 or more characters long' }),
type: z.string(),
....}
Let's say you want to use type with checkbox you would start with this
const {
register,
handleSubmit,
control,
setValue,
formState: { errors },
} = useForm<JobIForm>({
resolver: zodResolver(schema),
mode: 'onSubmit',
});
Then have you form component created like this
<div className='flex items-center justify-start flex-wrap gap-2'>
<div className='flex flex-col gap-4 grow-[1] shrink-1'>
<Label htmlFor='title'>Contract Type</Label>
<Controller
control={control}
name='type'
render={({ field: { onChange, value } }) => (
<Select onValueChange={onChange} value={value}>
<SelectTrigger>
<SelectValue placeholder='Choose contact type' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Contract Type</SelectLabel>
{types.map((type, index) => (
<SelectItem value={type} key={`key-${index}`}>
{type}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
)}
/>
{errors.type?.message !== undefined ? (
<p className='text-sm text-pink-600'>{errors.type.message}</p>
) : (
<p className='text-sm text-muted-foreground'>
This endicates the type of contract this job is for
</p>
)}
</div>
Upvotes: 0