LiveView form with atom as form data clears input fields on every event

With the form below, when I start typing into a field, the content of the other one gets cleared. Why is that? How do I prevent LiveView from clearing the input fields? Is it possible to do without a changeset?

defmodule HelloWeb.FormLive do
  use HelloWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, assign(socket, :form_params, "")}
  end

  def render(assigns) do
    ~L"""
    <%= f = form_for :user, "#", [phx_change: :validate, phx_submit: :save] %>
      <%= label f, :username %>
      <%= text_input f, :username %>
      <%= error_tag f, :username %>

      <%= label f, :email %>
      <%= text_input f, :email %>
      <%= error_tag f, :email %>

      <%= submit "Save" %>
    </form>

    <p>Form params:</p>
    <pre>
      <%= inspect(@form_params) %>
    </pre>
    """
  end

  def handle_event("validate", %{"user" => user}, socket) do
    {:noreply, assign(socket, :form_params, user)}
  end
end
2 Likes

You need to maintain the form state (values) on change, otherwise they will necessarily be cleared because the server re-rendered the form and no values from :user are there. So you need to take your @form_params and explicitly populate the inputs in your template.

3 Likes

Thanks, @chrismccord!
So here’s how this could be done:

defmodule HelloWeb.FormLive do
  use HelloWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, assign(socket, :form_params, %{})}
  end

  def render(assigns) do
    ~L"""
    <%= f = form_for :user, "#", [phx_change: :validate, phx_submit: :save] %>
      <%= label f, :username %>
      <%= text_input f, :username, value: @form_params["username"] %>
      <%= error_tag f, :username %>

      <%= label f, :email %>
      <%= text_input f, :email, value: @form_params["email"] %>
      <%= error_tag f, :email %>

      <%= submit "Save" %>
    </form>

    <p>Form params:</p>
    <pre>
      <%= inspect(@form_params) %>
    </pre>
    """
  end

  def handle_event("validate", %{"user" => params}, socket) do
    {:noreply, assign(socket, :form_params, params)}
  end
end
2 Likes