How to communicate with sticky child liveview?

Hi,

I have a sticky liveview, for a stateful navigation tree, which is shared across multiple pages. I navigate between the pages using link_patch() and would like the sticky liveview to get the params. Unfortunately, the handle_params() callback is not supported by child liveviews (sticky or not).

Currently, my convoluted solution is to generate a unique ‘tree_id’ in the handle_params() callback of the parent page (if it does not already exist in the URL) and pass it to the sticky liveview in the session. The navigation tree subscribes to that tree_id topic and appends the tree_id to every link_patch() URL.

On navigating, the handle_params() of the new parent page is called and I publish a message to the tree_id topic to inform the tree liveview of the params change.

I then use PubSub to send a message to the tree liveview to let it know the params have changed.

This works but seems needlessly convoluted. Is there an alternative I am missing?

I have to use a liveview for the tree as it receives handle_info() callbacks. It has to be shared between multiple pages (I initially used a single page for all possible routes but this became a behemoth).

The challenge appears to be that sticky liveviews are, by necessity, divorced from their parents so that they can persist between page changes.

Thanks in advance,

Gary

I tried this method once too and did not have any luck with it. You can ditch the sticky liveview, put a function component in your layout and add on_mount hooks to add the necessary functionality to all the pages.

2 Likes

Thanks for the fast reply.

I’m not sure I can go that way. I need a child liveview because I need the handle_info() callback. Also, I think on_mount() does allow me to inject state for many pages using a live_session() but I need to store that state somewhere outside of the component. I’ll keep reading as it’s more than likely I have misunderstood some nuances in the livebeats app.

I was hoping I had missed the really straightforward send(parent_id…) approach :slight_smile:

Gary

You attach a hook for the common handle_info.

https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#attach_hook/4

Thanks again.

I will give hooks a try :crossed_fingers:

Gave it a try…but still didn’t work as I’d hoped.

My understanding (after much IO.inspect()) is that

  • attaching hooks to the live session is a shorthand means of attaching hooks to every route within the session.
  • on navigating to a route, the hooks will run again.
  • The socket.assigns is reset prior to running the hooks so no information persists between navigations.

You were correct in suggesting attach_hook() to inject common handle_info() and handle_event() callbacks. That worked well.

Unfortunately, I am managing the folder tree using an in-memory digraph which is initialized once on mounting the sticky liveview. I need to persist the digraph between route changes (hence the desire to use a sticky liveview). The on_mount() hook regenerates the digraph each time so I lose the open/close state.

Honestly, I didn’t expect a shared stateful layout to be this challenging. I’m probably going to go back to a single liveview, a non-sticky liveview tree child and live actions to render the correct content.

Thanks for all your help.

Gary

I had the same problem, in a LiveView within a layout, I needed to send a message to my LiveView when clicking on a .link patch.

This is my hook:

Hooks.CloseMenu = {
  mounted() {
    this.el.addEventListener("click", e => 
      this.pushEvent("close_menu", {})
    )
  }
}

and my HTML/Heex code in the LiveView (which is embedded in a layout):

<.link patch={@to} class="p-4" phx-hook="CloseMenu" id={"link-#{@label}"}>...</.link>

and finally in my LiveView in handle_event:

def handle_event("close_menu", %{}, socket) do
  {:noreply, assign(socket, :menu_open?, false)}
end

Maybe this is a complete overkill solution for the problem, but I do not have any latency or and performance considerations to satisfy. If there is a more elegant solution, I’m always happy to learn it :smiley:.