LiveView 'plug' for shared logic?

I was wondering, imagine you have several (but not all) LiveViews where in the upon mount you would want to place some shared logic. For instance, you have an internal contract that every liveview will have a internal_id param on the session.

For now, in every LV I have code to fetch that value, transform it and put it on the socket assigns.

I was wondering if there would be a Plug-like alternative, where in the LV I could say

plug LiveViewHelpers.get_internal_id

Which would be called just prior to mount and update the assigns, to then call mount itself with the updated socket; kind of how plugs work in front of Phoenix controllers?

1 Like

The on_mount macro can do that and you can use live_session for sharing those across LV routes.

2 Likes

Thanks. I will look into that macro!

In router.ex

live_session :customer_portal,on_mount: [
    {AmplifyWeb.UnifiedUserAuth, :require_authenticated},
  ] do
  live "/portal/dashboard", CustomerPortalDashboard
end

In UnifiedUserAuth

def on_mount(:require_authenticated, _params, session, socket) do
    socket = mount_current_scope(socket, session)

    if socket.assigns.current_scope && socket.assigns.current_scope.unified_user do
      {:cont, socket}
    else
      socket =
        socket
        |> Phoenix.LiveView.put_flash(:error, "You must log in to access this page.")
        |> Phoenix.LiveView.redirect(to: ~p"/unified_users/log-in")

      {:halt, socket}
    end
  end
2 Likes

Thanks both again for your suggestions. Unfortunately we are not using live_session our live routes as of today, all LiveViews are kicked of by live_render. So for now, duplicating the logic accross LVs will still be the way to go I guess.

You might be able to keep things as tidy as possible with the on_mount macro if you haven’t used it in your code already: Phoenix.LiveView — Phoenix LiveView v1.1.17

There’s even an example that “Ensures common assigns” that seems to fit your use case.

Edit: Oopsie daisy, I didn’t see that this was suggested above. It’s worth reiterating I don’t think you need to use live_session to use the on_mount macro.

That’s right. While the docs give an example using live_session that’s just one way to do it, and calling the macro directly is documented earlier within the macro docs:

def on_mount(:user, _params, _session, socket) do
  {:cont, socket}
end

And then invoke it as:

on_mount {MyAppWeb.SomeHook, :user}

The “where to put the macro invocation” is mentioned in more detail elsewhere in the LiveView docs, refer to Security considerations — Phoenix LiveView v1.1.17

[…]

Now we can use the hook whenever relevant. One option is to specify the hook in your router under live_session:

live_session :default, on_mount: MyAppWeb.UserLiveAuth do
  # Your routes
end

Alternatively, you can either specify the hook directly in the LiveView:

defmodule MyAppWeb.PageLive do
  use MyAppWeb, :live_view
  on_mount MyAppWeb.UserLiveAuth

  ...
end

If you prefer, you can add the hook to def live_view under MyAppWeb, to run it on all LiveViews by default:

Since you don’t use live_session, you can still use the other two ways.

2 Likes