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

Client event selection_recovery fails when wrapped in input_for #96

Closed
atcherry opened this issue Jan 13, 2025 · 8 comments · Fixed by #97
Closed

Client event selection_recovery fails when wrapped in input_for #96

atcherry opened this issue Jan 13, 2025 · 8 comments · Fixed by #97
Labels
bug Something isn't working

Comments

@atcherry
Copy link
Contributor

atcherry commented Jan 13, 2025

LiveSelect and LiveView versions
have you tried LiveSelect's and LiveView's latest versions? Please try them and see if the bug still occur
which LiveSelect version are you on? 1.5.2
which LiveView version are you on? 1.0.1

I'm happy to create a PR for this.

Describe the bug
The selection_recovery event gets submitted to a parent LiveView instead of the LiveSelect LiveComponent for live selects wrapped in LiveView's inputs_for component.

Expected behavior
The LiveSelect hook's reconnected callback pushes the selection_recover event to the live select LiveComponent.

Actual behavior
The LiveSelect hook's reconnected callback pushes the selection_recovery event to the parent live view.

Screenshots
If applicable (in most cases it is), do add a screenshot (or even better, a GIF or a video) that describes the problem.

Browsers
Firefox

Issue Repo
The following repo demonstrates the issue.
https://github.com/atcherry/live_select_example

Additional context
When the reconnected callback for the LiveSelect hook gets called, somehow the element that is being referred to in the pushEventTo call lacks DOM information on where the live component is. This can be traced to the owner call in the Phoenix JS code for live_socket which makes a call to el.closest(...). el.closest(...) returns null.

I believe a simple but effective fix would be to call pushEventTo(this.el.id, ...) instead of pushEventTo(this.el, ...). I think I tested this and it worked, but I can make sure that is the case.

@maxmarcon maxmarcon added the bug Something isn't working label Jan 13, 2025
@maxmarcon
Copy link
Owner

Hi @atcherry! Thanks for reporting this and taking the time to dig into it.

Would you be able to produce a MR for this? Your proposed fix sounds good to me

@atcherry
Copy link
Contributor Author

Can do! I'm looking into more about what the exact scenario is that causes this. I tried reproducing this in a new project with no success with an unchanging number of inputs. That leads me to think that perhaps it is a bit more nuanced than just live_select being wrapped in inputs_for. Perhaps the issue is an inputs_for that renders a dynamic number of inputs. Regardless, I think the solution is still the same.

@thiagopromano
Copy link

thiagopromano commented Jan 16, 2025

We encountered the same issue, causing a crash loop on the LiveView process. I've tested replacing pushEventTo(this.el, ...)
with pushEventTo(this.el, ...) seems to fix it in our scenario too.

@thiagopromano
Copy link

I tested the fix in production and we started getting this error:

Failed to execute 'querySelectorAll' on 'Document': 'dashboard[our_form_name][2]_our_field_name_live_select_component' is not a valid selector.

On the line

      let targets = Array.from(dom.querySelectorAll(phxTarget))

Of phoenix_live_view/view.js.

At least the user browser doesn't stay on a crash loop.

@atcherry
Copy link
Contributor Author

atcherry commented Jan 27, 2025

@thiagopromano that is a problem. Sounds like you aren't giving your live select an id, so it generates the default id.
Perhaps id for live_select should be required instead of generating a default, though that does seems rather potentially breaking for other code bases. If you are indeed not setting your own id on the live_select component, then the id attribute shouldn't possibly generate an invalid one.

@maxmarcon thoughts?

@thiagopromano
Copy link

I would like to point out that in our use case, the form that contains the live_select lies in a popup that closes during a reconnect.

Therefore, it is understandable that the selection_recovery wouldn't work for us - the component no longer exists to be recovered.

I don't know if this is the same scenario in which you first encountered this issue, but if it is, I suppose the real solution would be to prevent sending the "selection_recovery" event altogether if the component is no longer in the DOM.

What do you think?

@atcherry
Copy link
Contributor Author

atcherry commented Jan 27, 2025

@thiagopromano yes, mine is a similar situation. Please refer to this repo for more information.

The issue is two fold. You need to make proper use of phx-auto-recover to restore your popup, but even after restoring the popup the original JS object reference this.el is gone from the DOM for reasons you pointed out. When making proper use of phx-auto-recover to restore the popup, the element id is preserved. The selection_recovery event can then be pushed to the right live component if the event is pushed to this.el.id instead.

That's the crux of the issue. Not sending the event if the component is no longer in the DOM would probably be a good thing to do, but you still need to reference the DOM element by id.

I encourage you to study that repo.

@maxmarcon
Copy link
Owner

maxmarcon commented Jan 27, 2025

I merged @atcherry MR (thanks again!) and released a new version as his changes are fixing the problem. Making form recovery work in the context of a popup that has disappeared from the DOM seems out of the scope of this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants