Recently I have a problem with my live, so I decided to create a minimal example to reproduce issue. However even after I did it I have no idea why my code have such behaviour …
I have started with phoenix_live_view.exs from mix-install-examples repository. The only change is to replace a template and handle_* callbacks with this code:
def render(assigns) do
~L"""
<textarea id="test1" phx-keydown="test"><%= @text %></textarea>
<textarea id="test2" phx-keydown="test"><%= @text %></textarea>
"""
end
def handle_event("test", %{"key" => key}, socket) do
{:noreply, assign(socket, :text, key <> "lixir")}
end
So what’s the issue? As in title the problem is with the assign, but only the element which fires the test keydown event. When I focus the #test1 and type E then in this textarea the content stays E and in #test2 it’s correctly changed to Elixir.
However if I change template to:
def render(assigns) do
~L"""
<div contenteditable id="test1" phx-keydown="test"><%= @text %></div>
<div contenteditable id="test2" phx-keydown="test"><%= @text %></div>
"""
end
Hmm, this might have to do with how focus is handled differently by div vs textarea HTML elements since LiveView won’t overwrite a focused input.
JavaScript client specifics
The JavaScript client is always the source of truth for current input values. For any given input with focus, LiveView will never overwrite the input’s current value, even if it deviates from the server’s rendered updates.
I see … so if I would like to do anything like that I would need to add a hidden textarea and work only on contenteditable element … At start I wanted to ask if someone have any idea how to change such behaviour, but I realized that’s fair enough at least for my use case.
Also great it’s mentioned in documentation. I did not found this as I was reading: Key Events in Bindings documentation instead.
Anyway, thanks for help. I would re-read entire Bindings documentation to make sure I did not missed anything important.
You could always push an event from the server side keydown event handler and explicitly overwrite the textarea with a client side hook that listens for said event. ¯\_(ツ)_/¯
Also, this might not be very practical… but if you use JS.focus to shift focus to another input alongside JS.push, then the previously focused textarea does get updated by LiveView.
Yeah, I was thinking about it, but it’s not about just one char (it was just a simple code to reproduce), so it causes more problems than solve.
This way I would lose the LV patching and therefore drastically increase the network usage. contenteditable maybe isn’t easiest to handle, but it have it’s own pros, so it’s ok for me to use it.
That’s said for other use cases it would be nice to be able to change the actual behaviour.