orsinium
Preserve params when push_patch from handle_event
I have 2 live components on the same page, one for pagination and another for search. Both write their state into URL query params which are later handled by the live controller.
def handle_event("pagination", %{"page" => _} = params, socket) do
path = ITJWeb.Router.Helpers.live_path(socket, ITJWeb.OffersLive, params)
socket = push_patch(socket, to: path)
{:noreply, socket}
end
def handle_event("offer_search", %{"search" => search}, socket) do
path = ITJWeb.Router.Helpers.live_path(socket, ITJWeb.OffersLive, search)
socket = push_patch(socket, to: path)
{:noreply, socket}
end
def mount(params, _session, socket) do
{:noreply, socket} = handle_params(params, nil, socket)
{:ok, socket}
end
def handle_params(params, _uri, socket) do
...
{:noreply, socket}
end
The issue is that each component resets all existing query params. In other words, when you change the page, the search gets reset. There is a live demo: itj.orsinium.dev/offers.
The issue is that live_path requires all query params to be explicitly passed as a third argument but the existing query params (if I understand correctly) are not passed into handle_event.
- Is there a way to get current URL query params inside
handle_event? - Is there a way to do
live_path+push_patchwith preserving the current URL query params? - Should I approach the whole problem differently? Is
handle_eventof a live component a good place to modify URL or should I delegate it to the controller somehow?
Marked As Solved
Sleepful
I did hit this, so now just use:
<.simple_form for={@form} action={~p"/"} >
instead of
<.form for={@form} action={Routes.home_path(@conn, :action, params)} >
and the :action from verified_route is whatever you defined in the router for that path
Also Liked
cmo
That’s certainly not the case. I have a LiveView that I set myriad URL query params on and they don’t get overridden. Though I keep the params in assigns so maybe I’m telling a fib ![]()
When you’re using the URL query params, you use handle_params to handle assigning anything that is associated with them. You don’t assign or update them elsewhere (i.e. in handle_event or handle_info). If you want to change them, you use push_patch.
You might find Sophie’s article on the topic useful, though she doesn’t seem to use push_patch.
def self_path(socket, extra \\ %{}) do
Routes.live_path(socket, __MODULE__, Enum.into(extra, socket.assigns.params))
end
defp patch(socket, extra) do
push_patch(socket, to: self_path(socket, extra))
end
def handle_params(params, _uri, socket) do
{
:noreply,
socket
|> assign_params(params)
|> assign_page(params)
|> assign_search(params)
|> assign_results()
}
end
defp assign_params(socket, params) do
assign(socket, :params, Map.take(params, ["page", "search"]))
end
defp assign_page(socket, %{"page" => page}) do
assign(socket, :page, String.to_integer(page))
end
defp assign_page(socket, _params) do
assign(socket, :page, 1)
end
def handle_event("submit", %{"search" => search_string}, socket) do
{:noreply, patch(socket, search: search_string, page: 1)}
end
def handle_event("goto_page", %{"number" => number}, %{assigns: %{page: page}} = socket) do
number = String.to_integer(number)
if number == page do
{:noreply, socket}
else
{:noreply, patch(socket, page: number)}
end
end
cmo
Or writing them as Function Components. Then you handle the event in the liveview. None of your components are managing their own state so could be Function Components.
orsinium
I see that people resurrected this thread with the release of Phoenix 1.7. So, probably, I should provide a final solution to it.
What I ended up doing is explicitly re-add search params to the new path when updating the page number:
And I do not re-add the page number when the search is updated because I want to get the user to the first page when they change the parameters anyway.
So, this solution is a bit messy (because the pagination component must know about all other components that change the page parameters, which is a good example of bad coupling) and you should avoid doing that in big projects. That was good enough for a small pet project, though.
People here (@cmo and @Sleepful) say that in Phoenix 1.7 verified routes should preserve GET parameters. I can’t check it right now (the project is still on Phoenix 1.6), so I’ll mark it as the answer. Feel free to drop a message in the thread if it doesn’t work for you.







