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
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
subsaharancoder
I’ve followed the Phoenix LiveView file upload code here Uploads — Phoenix LiveView v1.0.0-rc.7 and so far everything works just fine wit...
New
mohsen
I’m using an Umbrella project for a Phoenix application, and I want to have one Ecto Repo and one PostgreSQL database shared by all apps....
New
jaybe78
Hello, I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter). The diffic...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
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
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews