Using flash with liveview and conventional controllers

Hi all, I’m having trouble with showing my flash messages when using a mix of conventional phoenix controllers (to be able to set cookies) and liveviews. This is specifically happens when I am using the user log in/sign up workflow (which was pretty heavily tweaked from the phx.gen.auth code). The desired process looks like this:

/register (live view for validations) → POST to controller at /user/register, set http only cookie with session token → redirect to /home (liveview)

I’d like the flash messages to show on the /home liveview, but they aren’t.

Relevant code:

register_controller.ex:

defmodule MyAppWeb.RegisterController do
    use MyAppWeb, :controller

    ...

    def register(conn, %{"user" => user_params}) do
    with {:ok, %User{} = user} <- Accounts.register_user(user_params) do
      Accounts.send_user_registration_email(user)
      token = Accounts.generate_user_token(user, "session")
      user_return_to = get_session(conn, :user_return_to)

      conn
      |> renew_session()
      |> put_session(:user_token, token)
      |> put_session(:live_socket_id, "users_sessions:#{token}")
      |> put_flash(:success, "Welcome to MyApp, #{user.first_name}!")
      |> maybe_write_remember_me_cookie(token, user_params)
      |> redirect(to: user_return_to || signed_in_path(conn))
    end
  end
end

app.html.eex:

<main role="main">
    <div class="absolute flex flex-row my-3 w-screen justify-center items-center">
        <% success_msg = get_flash(@conn, :success) %>
        <% success_visibility = unless success_msg, do: "hidden", else: "" %>

        <div class="flex flex-row items-center rounded shadow border-2 border-green-300 mx-10 my-2 bg-green-200 <%= success_visibility %>" role="alert" phx-click="lv:clear-conn" phx-value-key="success">
            <p class="m-2 flex-grow text-green-700"><%= success_msg %></p>
            <svg class="w-5 h-5 mr-2" fill="#047857" role="button" viewBox="0 0 20 20"> <path d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>
        </div>
    </div>

    <div class="absolute flex flex-row my-3 w-screen justify-center items-center">
        <% info_msg = get_flash(@conn, :info) %>
        <% info_visibility = unless info_msg, do: "hidden", else: "" %>

        <div class="flex flex-row items-center rounded shadow border-2 border-blue-300 mx-10 my-2 bg-blue-200 <%= info_visibility %>" role="alert" phx-click="lv:clear-flash" phx-value-key="info">
            <p class="m-2 flex-grow text-blue-700"><%= info_msg %></p>
            <svg class="w-5 h-5 mr-2" fill="#1D4ED8" role="button" viewBox="0 0 20 20"> <path d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>
        </div>
    </div>

    <div class="absolute flex flex-row my-3 w-screen justify-center items-center">
        <% error_msg = get_flash(@conn, :error) %>
        <% error_visibility = unless error_msg, do: "hidden", else: "" %>

        <div class="flex flex-row items-center rounded shadow border-2 border-red-300 mx-10 my-2 bg-red-200 <%= error_visibility %>" role="alert" phx-click="lv:clear-flash" phx-value-key="error">
            <p class="m-2 flex-grow text-red-700"> <%= error_msg %> </p>
            <svg class="w-5 h-5 mr-2" fill="#B91C1C" role="button" viewBox="0 0 20 20"> <path d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>
        </div>
    </div>

    <%= @inner_content %>
</main>

live.html.leex:

<main role="main" class="bg-gray-50 h-screen overflow-scroll">
  <div class="absolute flex flex-row my-3 w-screen justify-center items-center">
    <% success_msg = live_flash(@flash, :success) %>
    <% success_visibility = unless success_msg, do: "hidden", else: "" %>

    <div class="flex flex-row items-center rounded shadow border-2 border-green-300 mx-10 my-2 bg-green-200 <%= success_visibility %>" role="alert" phx-click="lv:clear-flash" phx-value-key="success">
      <p class="m-2 flex-grow text-green-700"><%= success_msg %></p>
      <svg class="w-5 h-5 mr-2" fill="#047857" role="button" viewBox="0 0 20 20"> <path d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>
    </div>
  </div>

  <div class="absolute flex flex-row my-3 w-screen justify-center items-center">
    <% info_msg = live_flash(@flash, :info) %>
    <% info_visibility = unless info_msg, do: "hidden", else: "" %>

    <div class="flex flex-row items-center rounded shadow border-2 border-blue-300 mx-10 my-2 bg-blue-200 <%= info_visibility %>" role="alert" phx-click="lv:clear-flash" phx-value-key="info">
      <p class="m-2 flex-grow text-blue-700"><%= info_msg %></p>
      <svg class="w-5 h-5 mr-2" fill="#1D4ED8" role="button" viewBox="0 0 20 20"> <path d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>
    </div>
  </div>

  <div class="absolute flex flex-row my-3 w-screen justify-center items-center">
    <% error_msg = live_flash(@flash, :error) %>
    <% error_visibility = unless error_msg, do: "hidden", else: "" %>

    <div class="flex flex-row items-center rounded shadow border-2 border-red-300 mx-10 my-2 bg-red-200 <%= error_visibility %>" role="alert" phx-click="lv:clear-flash" phx-value-key="error">
      <p class="m-2 flex-grow text-red-700"><%= error_msg %></p>
      <svg class="w-5 h-5 mr-2" fill="#B91C1C" role="button" viewBox="0 0 20 20"> <path d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>
    </div>
  </div>

  <%= @inner_content %>
</main>

Oh, and note: the only time the flash messages do work and are rendered are in the incorrect user/pass login flow:

/login (liveview) → POST to /user/login → live_render(conn, MyAppWeb.LoginLive)

It still does not show the flash message when redirected to the /home liveview after logging in.

login_controller.ex:

defmodule MyAppWeb.LoginController do
    use MyAppWeb, :controller

    import Phoenix.LiveView.Controller, only: [live_render: 3]

    def login(conn, %{"user" => %{"email" => email, "password" => password} = user_params}) do
        with %User{} = user <- Accounts.get_user_by_email_and_password(email, password) do
          Accounts.send_user_registration_email(user)
          token = Accounts.generate_user_token(user, "session")
          user_return_to = get_session(conn, :user_return_to)

          conn
          |> renew_session()
          |> put_session(:user_token, token)
          |> put_session(:live_socket_id, "users_sessions:#{token}")
          |> put_flash(:success, "Welcome back, #{user.first_name}!")
          |> maybe_write_remember_me_cookie(token, user_params)
          |> redirect(to: user_return_to || signed_in_path(conn))
       else
          nil ->
              conn
              |> put_flash(:error, "Email/password combination incorrect")
              |> live_render(StreetHeroWeb.LoginLive, session: %{"email" => email})
       end
    end
end

What should I be doing differently to get the flash messages appear? And I guess am I even using redirect correctly? Should this instead be live_redirect? Thanks!

You are setting the flash in the @conn and this would get rendered in app.html, only issue is that liveview doesn’t uses app.html, but live.html.

1 Like

Both my app.html and live.html were posted above, and they both have the components to handle flash messages. I did some digging here, and I found that there is a test in the phoenix_live_view code base that indicates that my situation should work as I expect:

(this is the 0.15.7 release of liveview)

1 Like

Update: I figured out the problem - there were two. The flash does work as expected, but I had made some tweaks after posting this, and I changed it to an if statement that encapsulated the entire flash block. In this if, I forgot to use the <%= %> instead of <% %>. The other mistake was that my markup has an element underneath that has a z-index. So I set the z-index to max on this, and now it is showing up. My bad. :frowning:

3 Likes

I was about to mention the <%= :blush:, happy you figured it out!

1 Like