reflex-dev / reflex

🕸️ Web apps in pure Python 🐍
https://reflex.dev
Apache License 2.0
19.83k stars 1.14k forks source link

form.submit() fails if using dialog.close() #4221

Open mistamun opened 3 hours ago

mistamun commented 3 hours ago

Describe the bug form.submit() fails if using dialog.close()

To Reproduce

import reflex as rx

class FormState(rx.State):
    form_data: dict = {}

    def handle_submit(self, form_data: dict):
        """Handle the form submit."""
        self.form_data = form_data

app = rx.App()

@rx.page()
def index():
    return rx.dialog.root(
        rx.dialog.trigger(
            rx.button(
                rx.icon("plus", size=26),
                rx.text("Test Form in Dialog", size="4", display=["none", "none", "block"]),
                size="3",
            ),
        ),
        rx.dialog.content(
            rx.dialog.title(
                "Simple Form",
                weight="bold",
                margin="0",
            ),
            rx.form.root(
                rx.form.field(
                    rx.flex(
                        rx.form.label("Email"),
                        rx.form.control(
                            rx.input(
                                placeholder="Email Address",
                                # type attribute is required for "typeMismatch" validation
                                type="email",
                            ),
                            as_child=True,
                        ),
                        rx.form.message(
                            "Please enter a valid email",
                            match="typeMismatch",
                        ),
                        rx.flex(
                            rx.dialog.close(
                                rx.button(
                                    "Cancel",
                                    variant="soft",
                                    color_scheme="gray",
                                ),
                            ),
                            rx.form.submit(
                                rx.dialog.close(
                                    rx.button("Submit And Close"),  # Form submission cancelled
                                ),
                                as_child=True,
                            ),
                            rx.form.submit(
                                rx.button("Submit Without Closing"),  # Works as expected
                                as_child=True,
                            ),
                            padding_top="2em",
                            spacing="3",
                            mt="4",
                            justify="end",
                        ),
                        direction="column",
                        spacing="2",
                        align="stretch",
                    ),
                    name="email",
                ),
                on_submit=FormState.handle_submit,
                reset_on_submit=True,
            ),
            max_width="450px",
            padding="1.5em",
            border=f"2px solid {rx.color('accent', 7)}",
            border_radius="25px",
        ),
    )

Expected behavior The above is taken from one of the standard templates, I get a console error Form submission canceled because the form is not connected

I couldn't find a workaround.

Specifics (please complete the following information):

linear[bot] commented 3 hours ago

ENG-3985 form.submit() fails if using dialog.close()

masenf commented 3 hours ago

I tried to reproduce this issue and it actually appears to be working as intended on my end.

Tried with v0.6.1 and with current main. In both cases, if I entered a valid email address, then the form submit handler was called.

I did notice a bit of weirdness: when i enter an invalid email, the form validation fails, which blocks the submit... but the dialog still closes, so the user isn't really aware that there was a validation error, it just seems like nothing happened.

To work around this, you might need to use a flag in the FormState like dialog_is_open: bool, and have the dialog controlled via that state var; then instead of relying on rx.dialog.close, you can just flip the flag in the on_submit handler after everything goes through.

Some sample code:

import reflex as rx

class FormState(rx.State):
    form_data: dict = {}
    dialog_is_open: bool = False
    stay_open_after_submit: bool = False

    def handle_submit(self, form_data: dict):
        """Handle the form submit."""
        self.form_data = form_data
        if not self.stay_open_after_submit:
            self.dialog_is_open = False

app = rx.App()

@rx.page()
def index():
    return rx.dialog.root(
        rx.dialog.trigger(
            rx.button(
                rx.icon("plus", size=26),
                rx.text("Test Form in Dialog", size="4", display=["none", "none", "block"]),
                size="3",
            ),
        ),
        rx.dialog.content(
            rx.dialog.title(
                "Simple Form",
                weight="bold",
                margin="0",
            ),
            rx.form.root(
                rx.form.field(
                    rx.flex(
                        rx.form.label("Email"),
                        rx.form.control(
                            rx.input(
                                placeholder="Email Address",
                                # type attribute is required for "typeMismatch" validation
                                type="email",
                            ),
                            as_child=True,
                        ),
                        rx.form.message(
                            "Please enter a valid email",
                            match="typeMismatch",
                        ),
                        rx.flex(
                            rx.dialog.close(
                                rx.button(
                                    "Cancel",
                                    variant="soft",
                                    color_scheme="gray",
                                ),
                            ),
                            rx.spacer(),
                            rx.text("Keep Open?"),
                            rx.switch(
                                checked=FormState.stay_open_after_submit,
                                on_change=FormState.set_stay_open_after_submit,
                            ),
                            rx.form.submit(
                                rx.button("Submit"),
                                as_child=True,
                            ),
                            align="center",
                            padding_top="2em",
                            spacing="3",
                            mt="4",
                            justify="end",
                        ),
                        direction="column",
                        spacing="2",
                        align="stretch",
                    ),
                    name="email",
                ),
                on_submit=FormState.handle_submit,
                reset_on_submit=True,
            ),
            max_width="450px",
            padding="1.5em",
            border=f"2px solid {rx.color('accent', 7)}",
            border_radius="25px",
        ),
        open=FormState.dialog_is_open,
        on_open_change=FormState.set_dialog_is_open,
    ), rx.text(FormState.form_data.to_string())
mistamun commented 2 hours ago

Thank you, I was indeed using valid email addresses, in fact any type of field shows the same behaviour here

mistamun commented 2 hours ago

Is the following inverse nesting valid? It doesn't work for me either but I don't see the same console error.


rx.dialog.close(
    rx.form.submit(
        rx.button("Submit And Close"),  # Form submission cancelled
        as_child=True,
    ),
),