Search code examples
elixirphoenix-frameworkphoenix-live-view

How to make a variable globally accessible across multiple templates


In my phoenix app, I am trying to load a bunch of data and make them accessible to my root template (across all pages). To my knowledge, this is done via assigns, but I don't know where to load it.

I think something like

use App.LiveView
use App.News

assign(socket, news: News.all)

should work, but I don't know where to put this code, so the socket would be available. Coming from Express.js, I'm looking to do something like:

router.use((req, res, next) => {
  // Load news into global variable
  // ...
  next()
})

Thanks for helping.


Solution

  • The non-LiveView Phoenix equivalent of Express' router.use would be Router.plug/2. You could just add a plug in a pipeline.

    A plug could either be defined as a module or a simple function.

    Here is a minimal example:

      pipeline :browser do
        plug :accepts, ["html"]
        plug :fetch_session
        # ...
        plug :my_plug  # <= add your plug
      end
    
      def my_plug(conn, _opts) do
        Plug.Conn.assign(conn, :news, News.all())
      end
    

    But this wouldn't work with LiveView, which doesn't have access to plug assigns. Since LiveView 0.17, you can use a mount hook to achieve this. You need to add an on_mount/1 to your AppWeb.live_view/0.

    def live_view do
      quote do
        use Phoenix.LiveView,
          layout: {AppWeb.LayoutView, "live.html"}
    
        on_mount AppWeb.NewsLiveFetcher  # <= add this
        unquote(view_helpers())
      end
    end
    

    And implement AppWeb.NewsLiveFetcher something like:

    defmodule AppWeb.NewsLiveFetcher do
      import Phoenix.LiveView
    
      def on_mount(:default, _params, _session, socket) do
        socket = assign_new(socket, :news, &News.all/0)
        {:cont, socket}
      end
    end