Assign in textarea which fires event

Hi,

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

and add some basic styles to layout:

      div {
        height: 100px;
        width: 100px;
      }

      #test1 {
        background-color: cyan;
      }

      #test2 {
        background-color: pink;
      }

then both div elements have Elixir after pressing E key in any of element regardless of which one is firing event.

Is it a known bug or am I doing something wrong?

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.

source: Form bindings — Phoenix LiveView v0.19.3

Updated to add:

1 Like

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.

1 Like

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.

    <textarea id="test1" phx-keydown={JS.focus("#away") |> JS.push("test")}><%= @text %></textarea>
    <input id="away">
1 Like

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.

1 Like