ค้นหาเว็บไซต์

typescript - การทำงานกับหลายกล่องเลือกใน Nextjs14, zod, shadcn/ui และ react-hook-form


ฉันกำลังสร้างแบบฟอร์มให้ลูกค้า และหนึ่งในคำถามต้องการให้ผู้ใช้เลือกช่องทำเครื่องหมาย (หรือหลายช่องทำเครื่องหมาย) ฉันยังไม่เข้าใจโครงสร้างของ zod อย่างเต็มที่ ดังนั้นฉันจึงติดอยู่กับการจะทำสิ่งนี้ ฉันลองเปลี่ยนโครงสร้างหลายครั้ง ฉันแม้กระทั่งลองทำการแมปผ่านอาร์เรย์ของรายการที่ฉันต้องการให้ถูกเลือกและแสดงเป็นช่องทำเครื่องหมาย แต่ก็ไม่สำเร็จ

ฉันก็พยายามที่จะกำหนดสตริงให้กับกล่องเลือกด้วยเช่นกัน (ฉันไม่ได้ใช้มันสำหรับคำถามถูกหรือผิด)

นี่คือตารางฟอร์มของฉัน:

<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>
    )}
/>

สคีมาของฉันสำหรับฟิลด์เฉพาะนั้น: reservationMethod: z.string().array(),

คำตอบ #1

ฉันลองทำแบบนี้แล้วมันได้ผลสำหรับฉัน

ใช้รายการช่องทำเครื่องหมายเป็นคู่คีย์ค่าออบเจ็กต์

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;

คำตอบ #2

อีกวิธีหนึ่งในการใช้ส่วนประกอบ chadcn ui กับ react-hook-form ที่ฉันพบว่าง่ายคือการใช้ control.

const schema = z.object({
 title: z.string().min(5, { message: 'Must be 5 or more characters long' }),
 type: z.string(),
 ....}

สมมติว่าคุณต้องการใช้ type กับ checkbox คุณจะเริ่มด้วยอันนี้

const {
    register,
    handleSubmit,
    control,
    setValue,
    formState: { errors },
  } = useForm<JobIForm>({
    resolver: zodResolver(schema),
    mode: 'onSubmit',
  });

แล้วคุณก็ให้สร้างองค์ประกอบฟอร์มแบบนี้

<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>