LiveView: How to push_redirect in mount (without full page reloads)?

Hi everybody,
TL;DR: How to push_redirect in mount without a full page reload?

I have an application that handle localization with Gettext and I have a plug that set the selected locale in a cookie and in the session.

Now, say I have the following routes:

    live "/:locale", DashboardLive
    live "/product", ProductLive
    live "/cart", CartLive

Sometimes from the other live views I want to go back to the Dashboard, but I don’t want to track anymore the locale. So I have live_redirect links to "/" instead of for example "/en":

# In LiveView, e.g. product_live.html.leex
live_redirect "Home", to: Routes.live_path(@socket, MyApp.DashboardLive, "")

In my Dashboard mount however I want to redirect back to "/en" (or whatever is the current locale) but without refreshing the page.

Currently I have the following code in mount:

def mount(params, session, socket) do
    locale = params["locale"] || socket.assigns[:locale] || session["locale"]
    ...
    # In case of the current path being "/"
    socket = if params["locale"] == nil do
      push_redirect(socket, to: "/#{locale}")
    else
      socket
    end

    {:ok, socket}
  end

Here the code might not be idiomatic, but in my current code I also handle the case where the locale is not in the known locale list but removed for brevity…

The problem is that, I’m getting a full page reload at one moment, but I don’t know where?

If in the views I have the links with the locale, e.g.

# In LiveView, e.g. product_live.html.leex
live_redirect "Home", to: Routes.live_path(@socket, MyApp.DashboardLive, "en")

I can go back and forth within the LiveViews without full reloads.

So I wonder what I’m doing wrong?

Also, the redirect seems to happen after a little delay.
I mean, when I look in the network panel of the devtools, at first it seems that the redirect is actually happening within live views without HTTP full reloads, but after a second or two I got a full page reload…

Edit: At first I tried to use push_patch, but it seems that’s impossible to call push_patch in mount. I got the error attempted to live patch while mounting

Hello,

I reply to myself since I found a solution…
I simply do a push_patch but in handle_params and it works!

I still set the locale in assigns in mount, but now I have the following:

def handle_params(%{"locale" => _locale}, _url, socket) do
  {:noreply, socket}
end
def handle_params(_params, _url, socket) do
  {:noreply, push_patch(socket, to: "/#{socket.assigns.locale}")}
end

In the meantime I saw that it’s possible to check for existence (or absence) of keys in map from elixir 1.10.
So the following is also applicable.

def handle_params(params, _url, socket) when not is_map_key(params, "locale") do
  {:noreply, push_patch(socket, to: "/#{socket.assigns.locale}")}
end
def handle_params(_params, _url, socket) do
  {:noreply, socket}
end

I wanted to know which one do you prefer and think that it’s the most idiomatic elixir?
I want to say that both are idiomatic since there are both pattern matches.

I tend to prefer the second one in which what I want to do (push_patch) is explicit and matched on the guard I want (absence of key)

In the first one it’s implicit. I mean in the match I’m doing nothing, and then I do what I want in the default fallback match…

Anyway…

Have a nice day…
And happy elixir 1.11 ! (even if it’s still RC)

2 Likes