phoenixframework / phoenix_live_view

Rich, real-time user experiences with server-rendered HTML
https://hex.pm/packages/phoenix_live_view
MIT License
6.18k stars 930 forks source link

nodes with phx-update="ignore" are being mutated #647

Closed lnr0626 closed 4 years ago

lnr0626 commented 4 years ago

Environment

Elixir 1.10.1 (compiled with Erlang/OTP 22)

* Phoenix version (mix deps):

Actual behavior

The DOM updates applied after phx-change events when there are errors in the form seem to update elements that have phx-update="ignore". I have a form in my app that uses hooks to mount a stripe card element to collect payment information. I set phx-update="ignore" on the card-element div as Stripe's javascript controls the div's inner content as well as a few attributes on the div itself; and then use some standard text inputs to collect other required info (specifically the customer name).

When phx-change fires, and the form is valid, the updates are applied as i'd expect - namely the card-element div isn't touched. However, when the name field has an error; the card-element div is touched in a way that requires re-mounting the stripe element into the dom. This means that any user input in that field is cleared, and as stripe's javascript is setup to prevent sensitive input from leaking i don't have a way to repopulate it.

I setup some mutation observers to try to get better visibility it what was changing and how. There were definitely fewer updates being applied after a successful validation via phx-change compared to an unsuccessful one.

After some additional digging around; it appears that on a failed form validation, live view is merging the attributes for children of ignored containers; however on successful validation live view stops descending when it hits the phx-update="ignore" container.

Expected behavior

I would expect that putting phx-update="ignore" on an element would make that element be left alone; however I may just not have enough understanding of what's going on under the covers.

I'll put together a minimal app that showcases this sometime this weekend; however until I get to that (and for a little more context), the form is pretty much the first example in the stripe elements getting started guide (https://stripe.com/docs/stripe-js), with the exception of using hooks for handling the javascript portions

lnr0626 commented 4 years ago

I originally didn't have an ID on the container, and adding that fixed the extra updates I was seeing

However, the updates are still triggering stripe to re-mount; it looks like the ignored container is only having updates applied to it after my phx-change handler updates the assigns with a changeset that has errors.

lnr0626 commented 4 years ago

And surrounding the other input in a div fixed the issue I was seeing altogether. My guess is that morphdom was originally not able to tell that node hadn't changed as it didn't have an id; and then it was modifying the div after inserting the sibling error span.

chrismccord commented 4 years ago

Yes you need to give it an ID because while phx-update=ignore prevents updates, we cannot prevent a patch from removing it from the DOM. You were seeing it removed and recreated because when a sibling node is inserted above an element, the element is considered removed unless it has an ID because it's location in the parent children list changed.