ui: Form in dialog doesn't send the Request.

Hello, i have a little Problem.

so thats my Code:

    <Dialog open={open} onOpenChange={setOpen}>
      <Form {...form}>
        <form onSubmit={form.handleSubmit(onSubmit)}>
          <DialogContent className="sm:max-w-[425px]">
            <DialogHeader>
              <DialogDescription>
                Make changes to your profile here. Click save when youre done.
              </DialogDescription>
            </DialogHeader>

            <FormField
              control={form.control}
              name="full_name"
              render={({ field }) => (
                <FormItem>
                  <FormLabel>Name</FormLabel>
                  <FormControl>
                    <Input placeholder="Name" {...field} />
                  </FormControl>
                  <FormMessage />
                </FormItem>
              )}
            />
            <DialogFooter>
              <Button type="submit">Submit</Button>
            </DialogFooter>
          </DialogContent>
        </form>
      </Form>
    </Dialog>

That looks like

image

The Problem now is, that the Form doesnt send the Request. It should be, or am i wrong ?

When i move the <Form> into the <DialogContent> the Form works but the styles are wrong

image

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 8
  • Comments: 20

Most upvoted comments

IF YOU ARE USING ANY KIND OF FORM RESOLVER: Apart from this above solutions on form there might be issue in your zod schema.If you forgot to add an required zod field schema in the form then there would’nt be any error in form but internally zod stop you from submitting the form.

If you are rendering the form outside the Dialog.Content, what is happening is that the button is being portaled to the body and as it is not inside the form element the submit will not work. What you can do is:

  • Move the form into Dialog.Content
  • Use the form attribute on the submit button

To resolve this I moved the <Form {...form}/> component to wrap the entire <Dialog> component and placed the submit button inside the <DialogContent>.

Here’s the code:

const formSchema = z.object({
  is_business: z.boolean().default(false),
  // more schema here...
});

export default function NewCxDialog({ open, onOpenChange }) {
  const { handleSubmit, control, ...form } = useForm({
    resolver: zodResolver(formSchema),
    defaultValues: {
      is_business: false,
     // default values here...
    },
  });

  function onSubmit(e) {
    function closeDialog() {
      onOpenChange(false);
    }

    const submitData = async (e) => {
      try {
        const response = await fetch("/api/new/cx", {
          method: "POST",
          cache: "no-store",
          body: JSON.stringify(e),
          headers: {
            "Content-Type": "application/json",
          },
        });
      } catch (error) {
        throw error;
      }
    };

    // I use sonner for toasts
    toast.promise(submitData(), {
      loading: "Cargando...",
      success: () => {
        router.refresh();
        closeDialog();
        return "Cliente Creado Exitosamente";
      },
      error: (e) => {
        console.error(e);
        return "Hubo un error. Intenta nuevamente.";
      },
    });
  }

  return (
    <Form {...form}>
      <Dialog
        open={open}
        onOpenChange={() => {
          form.reset();
          onOpenChange();
        }}
      >
        <DialogContent className="max-w-2xl">
          <form onSubmit={handleSubmit(onSubmit)}>
            <DialogHeader>
              <DialogTitle className="!text-primary">
                Crear Nuevo Cliente
              </DialogTitle>
              <DialogDescription>
                Llena el formulario con la información de tu cliente.
              </DialogDescription>
            </DialogHeader>
            <div className="grid gap-6 py-4">
              <div className="grid grid-cols-2 gap-6">
                <FormField
                  control={control}
                  name="first_name"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>Nombre</FormLabel>
                      <FormControl>
                        <Input {...field} />
                      </FormControl>
                      <FormMessage />
                    </FormItem>
                  )}
                />
                <FormField
                  control={control}
                  name="last_name"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>Apellido</FormLabel>
                      <FormControl>
                        <Input {...field} />
                      </FormControl>
                      <FormMessage />
                    </FormItem>
                  )}
                />
               // more code here...
              </div>
            </div>
            <DialogFooter>
              <Button type="submit">Crear Cliente</Button>
            </DialogFooter>
          </form>
        </DialogContent>
      </Dialog>
    </Form>
  );
}

@daddotmn use button onClick and dispatch submit event with bubbles true

  <Form {...form}>
          <form ref={formRef} onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
              ...
          </form>
  </Form>
  ...
  <Button
    onClick={() => {
      if (formRef.current) {
        formRef.current.dispatchEvent(new Event('submit', { bubbles: true }));
      }
    }}
  >
    Continue
  </Button>

I found this: https://github.com/react-hook-form/react-hook-form/issues/566#issuecomment-612328736

There seem to be several solutions.

  1. If you have access to handleSubmit() function in the component which includes your submit button: invoke it directly handleSubmit(onSubmit)().
  2. You can set an id on your <form id="my-form" >, and add this id to the form attribute of your submit button: <button type="submit" form="my-form" />.

Option 1 seems easiest and solved my problem. Option 2 triggered a full page refresh/rerender so i didn’t use it.

I have a similar setup, but I’ve separated the Form into its own component and nested it into the Dialog as shown below:

"use client"

/* a bunch of imports you don't care about */

export function TableDialogCustom() {

  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button variant="outline">Add Table</Button>
      </DialogTrigger>
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <DialogTitle>Add a Table</DialogTitle>
          <DialogDescription>
            Add a table name and a read-only API key from AirTable
          </DialogDescription>
        </DialogHeader>
        <div className="grid gap-4 py-4">
          <div className="grid items-center gap-4">
            <TableForm />
          </div>
        </div>
      </DialogContent>
    </Dialog>
  )
}
"use client"

/* a bunch of imports you don't care about */

export function TableForm() {

    /* a bunch of resolver/onclick stuff you don't care about */

    return (
        <Form {...form}>
            <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
            <FormField
                control={form.control}
                name="table_name"
                render={({ field }) => (
                <FormItem>
                    <FormLabel>Username</FormLabel>
                    <FormControl>
                        <Input placeholder="Table Name" {...field} />
                    </FormControl>
                    <FormDescription>
                    Enter your table name here.
                    </FormDescription>
                    <FormMessage />

                </FormItem>
                
                )}
            />
            <FormField
                control={form.control}
                name="api_key"
                render={({ field }) => (
                <FormItem>
                    <FormLabel>API Key</FormLabel>
                    <FormControl>
                        <Input placeholder="API Key" {...field} />
                    </FormControl>
                    <FormDescription>
                    Enter your API Key here.
                    </FormDescription>
                    <FormMessage />
                    
                </FormItem>
                
                )}
            />
            <Button type="submit">Submit</Button>
            </form>
        </Form>
        )
}

It ends up looking like this: Screen Recording 2023-06-26 at 1 27 42 PM

@kgaurav152 did ya manage to solve that issue?

Yes it’s working for me. What I did is that I separately made the form and then imported that into the dialog component and it’s working since then, have been using this same approach wherever needed and it’s working.

 Const UpdateProfileForm

Const UpdateProfile
 <UpdateProfileForm/>

2. form=“my-form”

Thank you! Solved it, there is no need to wrap around Dialog box. There was a problem with zod resolvers in my case and that fixed the problem.