How to compose a path from a map?

Hi, a good saturday afternoon to take a walk, here is South Korea.

I have a map %{sort_by: :stars, sort_dir: :desc}, want to compose a path from it, ?sort_by=distance&sort_ord=asc , then, reload the page using {:noreply, push_patch(socket, to: ~p"/products/?sort_by=distance&sort_ord=asc")}. to show sorted list. How to do that?

Below is the code I am working.

PS
The latest LiveView 0.8 seems NOT reload page even though the content of socket.assigns has been changed in handle_params(). I guess it’s due to the new stream function.

In addition, Route.live_path/3 also seems NOT work in the latest LiveView even after changing use Phoenix.Router, helpers: true and alias it to Route.

Thank you for reading.

defmodule MarketWeb.ProductLive.Index do
 ....
  def mount(_params, _session, socket) do
    {:ok, **stream**(socket, :products, Catalog.list_products())}
  end

  def handle_params(params, _url, socket) do
 # params: %{sort_by: :stars, sort_dir: :desc}
    socket = sort_products_using_params(socket)

  ** this is what I want to make work**
    {:noreply, push_patch(socket, to: ~p"/products/?sort_by=distance&sort_ord=asc")}
  end
... omitted
end

router.ex

scope "/", MarketWeb do
    pipe_through([:browser, :require_authenticated_user])

    live_session :require_authenticated_user,
      on_mount: [{MarketWeb.UserAuth, :ensure_authenticated}] do
      live("/users/settings", UserSettingsLive, :edit)
      live("/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email)
      live("/guess", WrongLive)

      live("/", ProductLive) # ???
      live("/products", ProductLive.Index, :index)
      live("/products/new", ProductLive.Index, :new)
      live("/products/:id/edit", ProductLive.Index, :edit)
      live("/products/:id", ProductLive.Show, :show)
      live("/products/:id/show/edit", ProductLive.Show, :edit)
    end
  end

You can pass a map of query params to the route. From the docs:

params = %{page: 1, direction: "asc"}
~p"/posts?#{params}"

I presume you have a typo and sort_products_using_params takes the params as an argument?

Do you want to patch from handle_params or to it?

Hi, cmo
Thank you for the comment that solved my issue. But, there arose another issue: recursive calling the handle_params() and shut down.

def handle_params(params, _url, socket) do
    socket =
      socket
      |> parse_params(params)
      |> apply_action(socket.assigns.live_action, params)

    {:noreply, push_patch(socket, to: ~p"/products/?#{params}")}
  end

In my codes, handle_param() is called by handle_info() which is called by the update event from a live_component.

  def handle_info({:update, params}, socket) do
    handle_params(params, "/products", socket)
    {:noreply, socket}
  end

Would you explain again what “Do you want to patch from handle_params or to it?” means?

Below is my current code.
mount() displays unsorted product list, and I want to display sorted list via handle_info() and handle_params() in order.

defmodule MarketWeb.ProductLive.Index do
  def mount(_params, _session, socket) do
    {:ok, stream(socket, :products, Catalog.list_products())} # unsorted product list.
  end

  def handle_params(params, url, socket) do
    socket = apply_action(socket, socket.assigns.live_action, params)
    {:noreply, push_patch(socket, to: ~p"/products/?#{params}")}
  end

  def handle_info({:update, params}, socket) do
    # params = %{sorted_by: price, sorted_ord: desc}
    handle_params(params, ~p"/products/?#{params}", socket)
    {:noreply, socket}
  end

  defp apply_action(socket, :index, params) do
    socket
    |> assign(:page_title, "Listing Products")
    |> assign(:products, Catalog.list_products(params))  # sorted product list.
  end
end

The code below updates address bar, http://localhost:4000/products/?sort_by=stars&sort_dir=asc and IO.inspect shows sorting works. But, screen is NOT updated to show the sorted result.

I can’t find out what is problem yet.

defmodule MarketWeb.ProductLive.Index do

  def mount(_params, _session, socket) do
# unsorted product list
    {:ok, stream(socket, :products, Catalog.list_products())}
  end

  def handle_params(params, _url, socket) do
    {:noreply, apply_action(socket, socket.assigns.live_action, params)}
  end

  defp apply_action(socket, :index, params) do
    socket
    |> assign(:page_title, "Listing Products")
    |> assign(:products, Catalog.list_products(params))
    |> IO.inspect # **shows sorted product list.**
  end

  def handle_info({:update, params}, socket) do
    path =  ~p"/products/?#{params}"
    {:noreply, push_patch(socket, to: path, replace: true)}
  end
end

My Repo.all() works somewhat strange…

I delete the comment below until I find out what’s problem.
Thank you again, cmo

As you discovered, I was trying to draw your attention to the fact that you’re restarting the lifecycle every time you got to handle_params.

I haven’t worked with streams yet but I have a feeling that assigning over an existing stream is not what you want to be doing. You might like to try it just as a plain old boring assign first.

2 Likes