henrik

henrik

Hi!

I’ve got a tabbed interface currently with two separate LiveViews, each with their own live route (e.g. live "/one", OneLive and live "/two", TwoLive in the router).

The live layout uses live_redirect for the tabs that switch between the two.

I don’t love the slight delay when clicking a link and a LiveView re-mounts. I’d like for both LiveViews to stay loaded for the duration of the user’s session.

What’s a good option here to load both LiveViews at the same time but let me navigate between them?

Put them both in the same non-live page with two <%= live_render … %> and make JS tabs to hide/show one or the other? And write my own push state stuff if I want the URL to be different?

Or perhaps nested LiveView components, where the outer one is responsible for the tabs and does push state stuff to the URL via LiveView hooks?

Showing Posts 1 to 8

josevalim

josevalim

Creator of Elixir

I am doing it LiveComponents in my app. The parent LiveView does a simple mapping from the current action to the component name, similar to how the phx.gen.live uses the action to enable the modal. The router looks like this:

live "/one", TabbedLive, :tab1
live "/two", TabbedLive, :tab2

Then you can use live_patch between them, but you shouldn’t really see such a drastic difference compared to live_redirect.

10
Post #1
henrik

henrik OP

Thank you, José! Sounds like a good way of doing it - will give that a shot and see how it compares.

henrik

henrik OP

Started experimenting with this.

I’m realising that turning my LiveViews into components makes them a little more work, because I’ll have to pass their state in from the parent TabsLive, have to explicitly tell them to pass events to @myself, and they don’t have a handle_info.

But I guess that’s the trade-off – if I want to switch between multiple live thingies without re-mounting them, I assume they need to be components within a parent LiveView. I guess there’s no way to switch between two different LiveViews without unmounting/remounting, other than if I render both on a non-live page and hide/show them?

Just to check that I’ve understood the idea correctly (and if it helps someone else who finds their way here), this is what I’ve got so far.

Routes:

live "/one", TabsLive, :one
live "/two", TabsLive, :two

With this navigation in the live layout:

<%= live_patch("One", to: Routes.tabs_path(@socket, :one)) %>
<%= live_patch("Two", to: Routes.tabs_path(@socket, :two)) %>

And this in TabsLive:

defmodule MyWeb.TabsLive do
  use MyWeb, :live_view

  def render(assigns) do
    ~L"""
    Hello: <%= @live_action %>

    <%= case @live_action do %>
    <% :one -> %>
      <%= live_component @socket, MyWeb.OneComponent, id: :one %>
    <% :two -> %>
      <%= live_component @socket, MyWeb.TwoComponent, id: :two %>
    <% end %>
    """
  end

  @impl true
  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  # Needs to be defined for re-rendering to happen.
  def handle_params(_params, _uri, socket) do
    {:noreply, socket}
  end
end
henrik

henrik OP

Seems like I can do

<%= live_render @socket, RemitWeb.OneLive, id: :one %>

instead of

<%= live_component @socket, MyWeb.OneComponent, id: :one %>

Then when I navigate between the tabs, it does mount the components again, but it no longer does an Ajax request to “/one” like it did when I used live_redirect … for the links in the layout.

I can go even further and have TabsLive do

def render(assigns) do
  ~L"""
  <div style="display: <%= if @live_action == :one, do: "block", else: "none" %>">
    <%= live_render @socket, MyWeb.OneLive, id: :one %>
  </div>

  <div style="display: <%= if @live_action == :two, do: "block", else: "none" %>">
    <%= live_render @socket, RemitWeb.TwoLive, id: :two %>
  </div>
  """
end

Then it only mounts them once and hides/shows them, but all within LiveView.

When both LiveViews run at the same time, they can’t be relied on to set page_title anymore (the one that isn’t visible could suddenly set it based on some background event), but that responsibility fits well in a TabsLive anyway:

def handle_params(_params, _uri, socket) do
  socket =
    case socket.assigns.live_action do
      :one ->
        assign(socket, :page_title, "One")
      :two ->
        assign(socket, :page_title, "Two")
    end

  {:noreply, socket}
end

Would love some feedback on how sane this solution is :smiley:

josevalim

josevalim

Creator of Elixir

They can build their own state - you don’t have to do it in the parent. That’s what what I do in my case. Although you are correct on the @myself and handle_info bit.

You shouldn’t need to define mount/3 and handle_params/3 in the parent LiveView either. If you have to do so, it is definitely a bug, please open up an issuie. :slight_smile:

Regarding your new approach, it definitely works fine, but it does mean more processes are spawned.

dalerka

dalerka

Could you please show how to implement this use case in a better/efficient way?

Exadra37

Exadra37

Maybe you want to see how Surface implements tabs under the hood and replicate it, or just use Surface, that is an abstraction for web components on top of live view, made by @msaraiva.

dalerka

dalerka

Nice! Thank you for great example!

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
itzmidinesh
Hi all, I’m working on a Phoenix project where I need to make external HTTP requests and I’m trying to choose between Req and HTTPoison....
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
csmr
Hi all, I’m new here and would like to hear peoples views on my Elixir/Phoenix based game-engine! :smiley: Mutonex gamedesign was finish...
New
Stef-Verniers
Hello I’ve been coding in Elixir for almost two years now because of my current job where we implemented Elixir in our application. But ...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews