Is there any way to get the current path in live view?

I am working on web app with 2 languages (English, Arabic), it has two directions of course (RTL, LTR). I would like when the user click on the flag button (flags to represent languages), the page reload with the newly selected locale, for example he/she reading some post, and he clicked “English” then the same post page reloaded in English language. I implemented this with lifecycle hooks but I don’t like the implementation because I have a lot of views, I used case do to check the current socket.view and check for the action also in socket.assigns.live_action but the logic became sooo long. I know in Phoenix there is current_path/2 function but it works only inside controllers for conn but not for socket.

here is what I used and how I implemented and hope someone will direct me to the right way of implementing this.

defmodule AhramSchoolWeb.Platforms.AdminPlatform do
  import Phoenix.LiveView
  import Gettext, only: [with_locale: 2]
  import AhramSchoolWeb.Gettext, only: [pgettext: 2]
  alias AhramSchoolWeb.Router.Helpers, as: Routes

  def on_mount(:default, _params, _session, socket) do
    {:cont,
     attach_hook(socket, :current_page, :handle_params, &set_active_tab_and_locale_route/3)}
  end

  defp set_active_tab_and_locale_route(params, _url, socket) do
    current_locale = socket.assigns.locale
    locale = (current_locale == "ar" && "en") || "ar"

    {active_tab, route} =
      with_locale(current_locale, fn ->
        case {socket.view, socket.assigns.live_action} do
          {AcademicsLive, :index} ->
            {pgettext("sidebar-admin", "Manage"),
             Routes.academics_path(socket, :index, locale: locale)}

          {DashboardLive, :index} ->
            {pgettext("sidebar-admin", "Dashboard"),
             Routes.admin_dashboard_path(socket, :index, locale: locale)}

          {AdmissionLive.Index, :index} ->
            {pgettext("sidebar-admin", "Applicants"),
             Routes.admission_index_path(socket, :index, locale: locale)}

          {AdmissionLive.Show, :show} ->
            {pgettext("sidebar-admin", "Applicants"),
             Routes.admission_show_path(socket, :show, params["id"], locale: locale)}

          {CandidateLive.Index, :index} ->
            {pgettext("sidebar-admin", "Candidates"),
             Routes.candidate_index_path(socket, :index, locale: locale)}

          {CandidateLive.Show, action} when action in [:show, :add_note] ->
            {pgettext("sidebar-admin", "Candidates"),
             Routes.candidate_show_path(socket, action, params["id"], locale: locale)}

          {CandidateLive.Show, _} ->
            {pgettext("sidebar-admin", "Candidates"),
             Routes.candidate_show_path(socket, :show, params["id"], locale: locale)}
         .
         .
         .
         .
         
          {_, _} ->
            {nil, Routes.admin_dashboard_path(socket, :index, locale: locale)}
        end
      end)

    {:cont,
     socket
     |> assign(locale_route: route)
     |> assign(active_tab: active_tab)}
  end

So in the function above, I check the current view, current action and if any params, also I return Label to determine the current page label to be highlighted in Sidebar, also the web app has front website and back dashboard so I have REALLY a lot of views. Above I am showing only views for back dashboard.

and in router.ex file

live_session :admin, on_mount: AdminPlatform do
    scope "/admin/dashboard", AhramSchoolWeb.Platforms.Admin do
      pipe_through :browser

      live("/", DashboardLive, :index, as: :admin_dashboard)
      ..............
      ........
    end
end

https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#c:handle_params/3

handle_params/3 has current uri

example from phoenix_live_dashboard - phoenix_live_dashboard/page_live.ex

 @impl true
  def handle_params(params, url, socket) do
    socket =
      socket
      |> assign_params(params)
      |> dashboard_mount_path(url, params)

    maybe_apply_module(socket, :handle_params, [params, url], &{:noreply, &1})
  end

  defp dashboard_mount_path(socket, url, params) do
    %{path: path} = URI.parse(url)
    range = if params["node"], do: 0..-3, else: 0..-2

    mount_path = path |> String.split("/", trim: true) |> Enum.slice(range) |> Enum.join("/")
    mount_path = "/" <> mount_path

    update_menu(socket, dashboard_mount_path: mount_path)
  end

I suggest not letting the hook have the smarts, but only the boring logic and expect the hook params to deal with the parts better handled declaratively rather than through code trying to much.

E.g.

on_mount {LanguageSwitch, route: :admission_show_path, params: ["id"]}
def on_mount(opts, _params, _session, socket) do
    {:cont, attach_hook(socket, :current_page, :handle_params, &set_active_tab_and_locale_route(&1, &2, &3, opts))}
  end

defp set_active_tab_and_locale_route(params, _url, socket, opts) do
    current_locale = socket.assigns.locale
    action = socket.assigns.live_action
    locale = (current_locale == "ar" && "en") || "ar"
    {active_tab, route} =
      with_locale(current_locale, fn -> 
        params_list = Enum.map(opts[:params], fn key -> params[key] end)
        apply(Routes, opts[:route], [socket, action] ++ params_list ++ [locale: locale])
      end)
   …
end
1 Like

Interesting… but the first line of code is not clear for me, you want me to apply it on the live_session? Because if so, it will not work as I have many paths not only :admission_show_path.

You’ll need to maintain the different routes and their param structure in some place and I’d hook those not at the router level, but in the individual liveviews. Afaik there’s no great way to map the url/path known to liveview at runtime back to router helpers.