Updating dom on phx-change on text input

I am working on creating a multi-page application form.
I have decided to hide the next-button until there is an answer for each question on the page.
To detect when an answer is updated, I have tried the following:

  • Attaching a phx-blur to the inputs. This worked, but forced the user to exit the input, which wasnt intuitive enough in certain cases.

  • Attaching a phx-keyup to inputs. I could not find a way to attach a reference to indicate which input was being edited, so I could update the relevant answer.

  • Wrapping the inputs in a <form> with phx-change. This triggered the event correctly, but did not reload the DOM. I even converted the navbar into a live_component, and tried triggering a forced re-render with send_update/2.

Do you have any ideas on how to handle this case?

Thanks in advance :slight_smile:

This is the way to go. Can you share your live view / template (or a distilled version) so we can say what could be wrong? Thanks!

Hey @chrismccord appreciate your input :slight_smile:

I created the following to replicate the issue:

defmodule GladosWeb.LiveView.Test do
  use Phoenix.LiveView

  def render(assigns) do
    Phoenix.View.render(GladosWeb.MemberView, "test_application_page.html", assigns)
  end

  def mount(%{}, socket) do
    socket =
      socket
      |> assign(:show_button, false)
      |> assign(:answer, "")

    {:ok, socket}
  end

  def handle_event(
        "set_answer",
        %{"_target" => [target]} = values,
        socket
      ) do
    answer = values[target]

    socket
    |> assign(:show_button, answer != "")
    |> assign(:answer, answer)

    {:noreply, socket}
  end
end

<form phx-change="set_answer">
  <input type="string" name="input" value="<%= @answer %>" class="border" %>
  <br/>
  Show button?  <%= @show_button %>
</form>

image

image

When editing the answer, the socket is updated with the correct value for :show_button. However, this is not updated in the DOM.

socket
|> assign(:show_button, answer != "")
|> assign(:answer, answer)

{:noreply, socket}

seems like those assigns doesn’t go on the socket/reply eg you need

socket = socket
|> assign(:show_button, answer != "")
|> assign(:answer, answer)

{:noreply, socket}

this is a bit of OOP vs FP…

socket
|> assign(:show_button, answer != "")
|> assign(:answer, answer)

is basically a noop in FP - while in OOP it would most likely have mutated “socket”

1 Like

Oh wow yeah that was obvious. Thank you :blush:

1 Like

@outlog I stored the updated socket. That did not solve it unfortunately.

can you share your new code?

String input
The following code is when using phx-change in conjunction with a input of type string.
ezgif.com-video-to-gif (1)

<form phx-change="set_answer">
  <input type="string" name="input" value="<%= @answer %>" class="border" %>
  <br/>
  Show button?  <%= @show_button %>
</form>

defmodule GladosWeb.LiveView.Test do
  use Phoenix.LiveView

  def render(assigns) do
    Phoenix.View.render(GladosWeb.MemberView, "test_application_page.html", assigns)
  end

  def mount(%{}, socket) do
    socket =
      socket
      |> assign(:show_button, false)
      |> assign(:answer, "")

    {:ok, socket}
  end

  def handle_event(
        "set_answer",
        %{"_target" => [target]} = values,
        socket
      ) do
    answer = values[target]

    socket =
      socket
      |> assign(:show_button, answer != "")
      |> assign(:answer, answer)

    {:noreply, socket}
  end
end

select input
ezgif.com-video-to-gif (4)

defmodule GladosWeb.LiveView.Test do
  use Phoenix.LiveView

  def render(assigns) do
    Phoenix.View.render(GladosWeb.MemberView, "test_application_page.html", assigns)
  end

  def mount(%{}, socket) do
    socket =
      socket
      |> assign(:show_button, false)
      |> assign(:answer, "hide")

    {:ok, socket}
  end

  def handle_event(
        "set_answer",
        %{"_target" => [target]} = values,
        socket
      ) do
    answer = values[target]

    socket =
      socket
      |> assign(:show_button, answer == "show")
      |> assign(:answer, answer)

    {:noreply, socket}
  end
end
<form phx-change="set_answer">
  <select name="select" class="mb-4">
    <option <%= if @answer == "hide", do: "selected" %> value="hide">hide button</option>
    <option <%= if @answer == "show", do: "selected" %>  value="show">show button</option>
  </select>
  <br/>
  Show button?  <%= @show_button %>
</form>

I don’t think you are properly pattern matching in your handle_event. If you do a print to the console inside the function, does it gets printed to the console?

If my suspicion is right, start with a bare values in the function head, print that to the console, see the format and then refine your pattern matching.

Yes it does in fact call the function, @sfusato :frowning:

check your browser console - believe there is a bug in phoenix_live_view.js
InvalidCharacterError: The string contains invalid characters.

ok, did the debugging…

error is the “%” at the end of your:
<input type=“string” name=“input” value="<%= @answer %>" class=“border” %>

that breaks phoenix_live_view.js - remove that and you are good to go…

btw type=“string” is not standard https://www.w3schools.com/tags/att_input_type.asp so maybe use “text”

Thank you, @outlog that fixed it :slight_smile:

1 Like