Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disabling event delegation by default breaks client-side validation examples #3457

Open
gbj opened this issue Jan 8, 2025 · 0 comments
Open

Comments

@gbj
Copy link
Collaborator

gbj commented Jan 8, 2025

Describe the bug

0.6 and previous used event delegation by default, but used regular event listeners for events added to components, which had the nice side effect that you could use on:submit to prevent the default behavior for forms from running, as is documented here.

Now, everything uses plain old event listeners by default, which means that the ActionForm's own event listener runs first, before the listener you add with on:submit. This makes it impossible to .prevent_default() successfully, as your code runs after the ActionForm has already run.

There are a few possibilities to address this:

  1. The user can enable the delegation feature on Leptos, which reenables event delegation by default.
  2. I can special-case ActionForm's submit to run on the next tick, so that when it checks if its default has been prevented any other listeners will already have run.
  3. I can add a general-case :capture flag (so on:submit:capture) to allow event listeners you add to run during the capture phase.

Option 1 reverts to the (working) 0.6 behavior, option 2 is a special-case hack, option 3 is a more general solution.

Leptos Dependencies

Any 0.7

To Reproduce

// Renders the home page of your application.
#[component]
fn HomePage() -> impl IntoView {
    // Creates a reactive value to update the button
    let count = RwSignal::new(0);
    let on_click = move |_| *count.write() += 1;

    view! {
        <h1>"Welcome to Leptos!"</h1>
        <button on:click=on_click>"Click Me: " {count}</button>
        <AddTodo/>
    }
}

#[component]
fn AddTodo() -> impl IntoView {
    let add_todo = ServerAction::<AddTodo>::new();

    let on_submit = move |ev: SubmitEvent| {
        let data = AddTodo::from_event(&ev);
        // silly example of validation: if the todo is "nope!", nope it
        let valid = match data {
            Ok(d) if d.title == "nope!" => {
                logging::log!("Invalid: {}", d.title);
                false
            },
            d @ Ok(_) => {
                logging::log!("Validated: {}", d.unwrap().title);
                true
            },
            Err(e) => {
                logging::log!("Error: {}", e);
                false
            },
        };

        if !valid {
            ev.prevent_default();
        }
    };

    view! {
        <ActionForm action=add_todo on:submit=on_submit>
            <label>
                "Add a Todo"
                // `title` matches the `title` argument to `add_todo`
                <input type="text" name="title"/>
            </label>
            <input type="submit" value="Add"/>
        </ActionForm>
    }
}

#[server]
pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
    logging::log!("Submitted: {}", title);
    Ok(())
}

Additional context
This can be fixed by enabling the delegation feature on Leptos, which reenables event delegation by default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant