Is it possible to render multiple instances of a LiveView module and have their states isolated from each other?

I’m trying out LiveView for a new pet project and I’m starting small with just rendering a button that can be clicked.

defmodule MyProject.Live.Toggle do
  use Phoenix.LiveView
  use Phoenix.HTML

  def mount(_session, socket) do
    {:ok, assign(socket, toggled: false)}
  end

  def render(%{toggled: false} = assigns) do
    ~L"""
    <div>
      <button phx-click="toggle">
        Click me to toggle!
      </button>
    </div>
    """
  end

  def render(%{toggled: true} = assigns) do
    ~L"""
    <div>
      <button phx-click="toggle">
        Cool. Now click again to toggle back!
      </button>
    </div>
    """
  end

  def handle_event("toggle", _, socket) do
    {:noreply, update(socket, :toggled, &(!&1))}
  end
end

And then rendering it within another template.

<%= live_render(@socket, MyProject.Live.Toggle) %>

Simple enough. The button shows up and it toggles between the two states when I click it. Great!

toggle_1

But – I need more than one togglable button on my website so I figured that I would just render another one in my wrapper template.

<%= live_render(@socket, MyProject.Live.Toggle) %>
<%= live_render(@socket, MyProject.Live.Toggle) %>

This doesn’t really behave as I would expect.

toggle_2

I expected the two buttons to be isolated from each other and have their own separate states. Is there a way to achieve this?

Hi, it looks like this is a known bug. The bug and workaround are discussed here:

Basically you need to supply a child_id to uniquely identify the liveview instances.

3 Likes

Thanks @mindok! Setting the child_id worked like a charm! :raised_hands:

1 Like