Is it possible to get the previous url in liveview?
E.g. when I click a link using live navigation, on the new page I’d like to know where I came from.
This is usually the job of the Referer header, but as far as I understand with live navigation those headers aren’t sent, as it goes over the web socket.
My use case is for view transition between pages. I’d like to know if I’m coming from a “sibling”, “child” or “parent” page and use a different animation for each.
I’ve seen this thread, but doing this workaround of passing the url to all links as a query parameter is not an acceptable solution for me.
I think I can solve my specific use case via JS and the history API somehow, but ideally I’d like to have this information on the server, as that’s where all the router logic should live.
Is it possible?
Could you use flash for that?
Yes. On the client side, LiveView emits a JavaScript event phx:navigate for live patch and live navigate URL changes.
As you said, you can use that and/or the history API to keep track of the current location (and indirectly know your “previous location”, though the event itself doesn’t include it).
On the server side, you could use attach_hook/4 to implement a handler for the :handle_params lifecycle stage. At that stage you have the current URI, and so, similarly, you could track where you’re coming from by looking at where you’re going to.
On the “disconnected mount/render”, you could use a Plug to save the Referer (when present) to an X-Referer or so header, so that you can then access it later with get_connect_info/2, and for live navigation/patch you’d need to keep track of the last known URI a given user has visited (you’d need to decide how and for how long to store this state). If getting cooperation from the client is okay, I think it’d be much easier to pass the “last URL” as a connect param: see the get_connect_params/1 example where you pass a params function to LiveSocket.
Could you elaborate? I don’t understand how that could work.
How could I track this? Storing where each user was in ETS or the DB or something? That wouldn’t work because a user can use multiple browsers, tabs etc. Or how exactly could I track it?
get_connect_info doesn’t really help, as it won’t change after a live navigation.
1 Like
Oh, actually get_connect_info might do the trick, as it looks like liveview already automatically tracks the previous URL!
get_connect_params(socket)["_live_referer"] Gives me the previous URL if called in the mount callback.
Though I’m not sure if I’m supposed to read out that value? It’s documented under the reserved params.
I read this as I shouldn’t override these values, but reading them is fine I guess?
2 Likes
Ok, the get_connect_params + keeping track of url changes in handle_params did the trick for me.
Thanks @rhcarvalho for getting me on that track!
I now created an on_mount hook:
def on_mount(:default, _params, _session, socket) do
prev_url = get_connect_params(socket)["_live_referer"]
{:cont,
socket
|> assign(prev_url: prev_url)
|> attach_hook(:view_transition_handler, :handle_params, &Session.handle_params/3)}
end
def handle_params(_unsigned_params, uri, socket) do
{:cont,
socket
|> assign(
prev_url: socket.assigns[:current_url] || socket.assigns.prev_url,
current_url: uri
)
|> maybe_start_view_transition()}
end
defp maybe_start_view_transition(socket) do
if socket.assigns[:__schedule_view_transition] do
socket
|> assign(__schedule_view_transition: false)
|> push_event(
"start-view-transition",
%{type: view_transition_type(socket.assigns[:prev_url], socket.assigns[:current_url])},
dispatch: :before
)
else
socket
end
end
defp view_transition_type(prev_url, current_url) do
...
end
And then I trigger transitions via a simple socket |> assign(__schedule_view_transition: true).
at first I tried to trigger transitions directly in my handle_event callbacks that result in a push_patch, but at this point I do not have the current url yet, hence the __schedule thingy to delay that until all information is present in the assigns
5 Likes
Does LV actually use that referer internally for something?
I wonder if it would be a good idea to add a get_live_referer() or similar. That’s kinda what static_changed?/1 does, but it has additional logic so maybe it’s more justified there.
1 Like
I had forgotten about it in my previous reply, but it is documented alongside get_connect_params/1:
So, yes, it can be used directly (and there is no framework-internal use for it).
The trail of how it gets set and sent to the server:
3 Likes