BreadCrumb component in LiveView

Creating a BreadCrumb component that renders the proper links was a piece of cake but I had the URL/request_path hardcoded during the development of rendering it.

Now I am completely flummoxed as to how to get the the current URL/request_path into my component. I have tried both as a live_component and a Phoenix.Component. While I can get to request_path at various points in the lifecycle there does not seem to be an idiomatic solution for the driving functionality within components based on the URL/request_path. It seems like the best place would be to solve this in a live_session on_mount call.

assign(:request_path, socket.private.connect_info.request_path)
this does work but it is not presented down to my component

Is there a place that the current path exists regardless of activities via “patch”, “push_patch”, 'navigate", and “push_navigate”?

1 Like

Have you considered hooking into LiveView’s handle_params/3 callback to keep the request_path assign updated?

The handle_params/3 callback is invoked after mount/3 and before the initial render. It is also invoked every time <.link patch={...}> or push_patch/2 are used. It receives the request parameters as first argument, the url as second, and the socket as third.

source: Live navigation — Phoenix LiveView v0.20.2

Hmm, are you passing the assign down to the component? It must be passed explicitly for it to be available.

I would expect socket.private.connect_info.request_path to be that place. You’d probably want to set it as an assign in mount callback and then keep it updated in handle_params callback.

1 Like

Confirm, handle_params is what you want. The second argument its he request URI. You can even set up a lifecycle hook to always place a @request_path assign into every LiveView to pass down to your components

4 Likes

Thank you both for the quick reply!

I did think about that but doesn’t mean that the breach_crumb would have to be a liveview rather than a component? If that is the case how do you inject a new liveview into app.html.heex?


<main>
  <div class="mx-auto max-w-3xl">
    <.flash_group flash={@flash} />
    <%= IO.inspect(@request_path, label: "app.html.heex") %>
    <%= @inner_content %>
  </div>
  <.bread_crumb_goes_here/>
</main>

Again thanks for your help!

app.html.heex has access to the assigns of the LV using the layout. It can stay a component.

2 Likes

Okay so that does work but that means the URL parameter needs to be via the assigns for every liveview. Is there a more global way to handle this?

Thanks! This will get me moving again!!

LV hooks can do that (the server side ones).

no it can be rendered wherever you want. If using a LC, the parent would pass in the @requet_path assign that was set from the lifecycle hook.

1 Like

This is how I handled this for a side Nav component that needed to track the “active” link:

defmodule MyAppWeb.Path do
  @doc """
  Sets the path from the connection as :current_path on the socket
  """
  def on_mount(:put_path_in_socket, _params, _session, socket),
    do:
      {:cont,
       Phoenix.LiveView.attach_hook(
         socket,
         :put_path_in_socket,
         :handle_params,
         &put_path_in_socket/3
       )}

  defp put_path_in_socket(_params, url, socket),
    do: {:cont, Phoenix.Component.assign(socket, :current_path, URI.parse(url).path)}
end

Then in your router in the live session you add it:

live_session :require_authenticated_user,
      on_mount: [
        {MyAppWeb.UserAuth, :ensure_authenticated},
        {MyAppWeb.Path, :put_path_in_socket}
      ] do

Then you can access it in your app.html.heex layout as @current_path:

<main class="flex w-full h-screen">
  <.flash_group flash={@flash} />
  <MyAppWeb.Nav.sidebar current_path={@current_path} current_user={@current_user} />
  <div class="w-full h-full p-4 overflow-y-auto bg-gray-50 dark:bg-gray-900">
    <%= @inner_content %>
  </div>
</main>
13 Likes

Whooo Hooo! You guys rock!!

This is so cool!!

This will clean up a bunch of crap!!

Thank you! Thank you! Thank you!

1 Like

You rock.