Phoenix LiveView does not re-render for a phx-submit triggered event, but works as expected for phx-click

The liveview does not re-render after a phx-submit triggered "save" event. However,
it works as expected during a "dec" event.

Could it have to do with the functions being called from TodoLists.Catalog?

I have used IO.inspect to verify that socket.assigns changes before and after assign().

defmodule TodoLists.TodoListLive do
  use TodoLists, :live_view

  alias TodoLists.Catalog

  on_mount {TodoLists.UserAuth, :current_user}

  def mount(_params, _session, socket) do
    user = socket.assigns[:current_user]
    todo_lists = Catalog.list_lists(user)

    {:ok,
     socket
     |> assign(:lists, todo_lists)
     |> assign(:val, 0)
    }
  end

  def handle_event("dec", _, socket) do
    {:noreply, assign(socket, val: socket.assigns.val - 1)}
  end

  def handle_event("save", %{"todo_list" => list_params}, socket) do
    user = socket.assigns[:current_user]

    case Catalog.create_list(user, list_params) do
      {:ok, todo_list} ->
        socket
        |> IO.inspect(label: "BEFORE")

        {:noreply,
         assign(socket, :lists, [todo_list | socket.assigns.lists])
         |> IO.inspect(label: "AFTER")}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply,
         socket
         |> assign(:changeset, changeset)}
    end

    {:noreply, socket}
  end

  def render(assigns) do
    ~H"""
    <h1>The count is: <%= @val %></h1>
    <button phx-click="dec">-</button>

    <span>
      <b>Create New Todo List:</b>

      <.form let={f} for={@changeset} phx-submit="save">
        <%= if @changeset.action do %>
          <div class="alert alert-danger">
            <p>
              Oops, something went wrong!
            </p>
          </div>
        <% end %>

        <%= label(f, :title) %>
        <%= text_input(f, :title) %>
        <%= error_tag(f, :title) %>

        <button type="submit">
          Save
        </button>
      </.form>
    </span>
    <table>
      <thead>
        <tr>
          <th>List</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <%= for list <- @lists do %>
          <tr>
            <td><%= list.title %></td>
          </tr>
        <% end %>
      </tbody>
    </table>
    """
  end
end

Elixir always returns the last statement, the whole case is being discarded because you’re calling {:noreply, socket} at the end of the function, remove it.

3 Likes