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

Fix submit form using magic actions without breaking form #8241

Conversation

danie-ramdhani
Copy link
Contributor

Based on discussion #8194

Scenario:

  • component has form
  • submit form using magic action (i.e. $parent)

Problem: all elements inside form is stuck with the input as readonly and the button as disabled

@calebporzio
Copy link
Collaborator

The problem

Consider the following simple form submission in a Livewire component:

<form wire:submit="savePost">
    ...

    <button type="submit">Submit<button>
</form>

By default, when the "Submit" button is pressed, Livewire will send a request to the server to run the "savePost" action. To ensure the form is not submitted another time while the request is out to the server, Livewire automatically marks form inputs as "readonly" and submit buttons as "disabled".

The problem is when the submit event triggers an action on the parent component, the form is marked as readonly/disabled, but is never set back to normal when the request comes back from the server.

Here's an example of a template containing the issue:

<form wire:submit="$parent.savePost">
    ...

    <button type="submit">Submit<button>
</form>

(Notice wire:submit="$parent.)

The cause

Here's a brief explanation of the system that disables forms in Livewire:

  • Detect a form element with wire:submit on it
  • Register a "submit" event listener with a "handler"
  • When a submission occurs:
    • walk the DOM tree of the form, marking elements as readonly/disabled
    • everytime an element is marked, a closure is created to "undo" the disable
    • those closures are pushed onto a Map by the component ID
  • When a network request is received from the server
    • Lookup any "undo" closures based on the request's component ID
    • Run them to "undo" the form

Normally this system works fine, however you may have spotted the problem:

The form elements are marked as disabled based on the component ID where the form resides, and the "undo" callbacks are looked up based on the component ID of the request to the server.

Therefore, because the <form> lives on component A, and the actual request is sent to component B, there is a mismatch, causing the form to become disabled and never un-disabled.

The solution

Before describing individual solutions, let's explore a few paths based on the nature of the problem:

We know the "disable" and "undo" steps are mismatched, so we either need to:

  • Match them
  • Or make the system more generic and not component specific

Solution A) Write a more targeted "undo" hook

The ideal solution would be to somehow detect which component the wire:submit request is being sent to, and register the "undo" callbacks based on that component instead of the <form> component.

Pros:

  • Solid, a more robust system than what's there right now

Cons:

  • Technically challenging and may require adding code in other areas

Solution B) Write a more generic "undo" hook

Another solution would be to run ALL undo callbacks when a network request is received instead of only targeting by specific elements.

Pros:

  • Extremely simple
  • Even more fault tolerant

Cons:

  • Would introduce buggy scenarios like:
    • two forms in two different components are submitted
    • one request comes back early
    • both forms are "reset" before they're ready
  • The con might be worth the pros in this case as the bug is kind of edge casey

Solution C) Write a conditional for $parent specifically

Another option is straight up hard-coding a conditional for $parent and using the component's parent as the hook for looking up "undo" callbacks.

Pros:

  • Simple

Cons:

  • Not an exhaustive solution

I'm honestly leaning towards C right now because I don't see another way to solve the problem without introducing more problems or making other invasive changes.

@calebporzio calebporzio merged commit 224c2f0 into livewire:main Apr 23, 2024
5 checks passed
@danie-ramdhani danie-ramdhani deleted the browser-test-submit-form-to-parent-component branch April 24, 2024 07:18
@danie-ramdhani danie-ramdhani changed the title Add browser test Can submit form using magic actions without breaking form Fix submit form using magic actions without breaking form Apr 24, 2024
@joshhanley joshhanley mentioned this pull request Apr 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants