LiveView navigation from LiveComponent

Hi,

Maybe, let me start with a code snippets:

In the root.html (non liveview) I have:

              <%= live_render(@conn, AppWeb.NotificationsLive,                                    
                session: %{"current_user_id" => @current_user_id}
              ) %>

That AppWeb.NotificationsLive renders like this:

  def render(assigns) do
    ~H"""
    <.modal :if={@show_notifications_list}>
      <.live_component
        module={AppWeb.NotificationsLiveList}
        id="notifications_list"
        notifications={@notifications}
        current_user_id={@current_user_id}
      />
    </.modal>

    <a href="#" phx-click="show_notifications">
      <i>
      ...
      </i>
    </a>
    """
  end

and can handle event like this:

  def handle_event("click_notification", %{"id" => id} , socket) do
    Notifications.set_seen_notifications(socket.assigns.current_user_id)
    Notifications.set_visited_notification(id)
    ...
    {:noreply, socket}
  end

That AppWeb.NotificationsLiveList modal is just a list of notifications for user (each notification in a div).

Problem:
If user clicks on given notification, then such action is noted and database is updated, that for given user, this notification will have e.g. different background color. But also when user clicks on the notification, then such click should also navigate her to different page - lets assume it is a LiveView with live route in router.ex and I want to use the existing socket for that push. How to do it? Wrapping div with notification in a <.link> does not work for me: the click_notification event will not be delivered, but the page to which notification is navigating will be loaded, for example:

  <.link replace patch={@n.link} phx-click="click_notification" phx-value-id={@n.id}>
    <div>
      ...
    </div>
  </a>

I’ve also tried push_navigate from the event handler (passing the link to the server via phx-value-link) - it is not working as I would expect: it is slow and there is new socket created (but I guess this is caused by some error, but I do not see any).

Why is the notification component in the root layout and not the layout template? As it stands right now the notification LV has no real relationship to the LV for the page, which would allow you to use live navigation.

The root.html in my case defines a header and footer on all pages (regardless if they are generated from LV or not) and the notification icon (and the whole notification modal when visible) is part of the header on all pages. That is/was my idea.

Hmm, it sounds like you may be mixing up push_navigate/2 and push_patch/2. The former shuts down the current LiveView and mounts a new one while the latter navigates within the existing LiveView. Try switching to push_patch/2 instead and potentially call the Notications.set_x functions asynchronously if they hold up the live navigation response longer than you’d like.

I think I can’t use push_patch since the AppWeb.NotificationsLive is not mounted as live in router.ex. Maybe in the end I will try to mount it and give it a try (but list of notifications are just a modal that user sees, I do not see benefit of having it in router).

But I still wonder: why I can’t navigate using <.link> (this is working - the page changes) and have the event handled in the AppWeb.NotificationsLive (this is not working, the code for handling click event is not run)?

The default <.link> component from Phoenix.Component doesn’t recognize the phx-click attribute and strips it from the rendered <a> tag.

It’s probably a good thing too as allowing bindings on the default <.link> component could lead to a race condition of sorts where one click results in two separate events – a live navigation event as well as a generic click event.

1 Like