Stream not prepending or resetting when expected

Hi everyone,

I’m working with a stream of a simple list with “songs” being prepended or appended to the list.

I’m able to see the code-paths being hit, where the stream is supposed to either reset or prepend data. Unfortunately, only appending works as expected, and prepending and resetting does not. Because I’m persisting the list prior to updating the stream, when I refresh, I see the correct order.

Can you help me diagnose what I’m doing wrong? I’ve also attached a screenshot of the phenomenon for the resetting case.

Package versions:

      {:phoenix_html, "~> 3.3"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.20.14"},

The following code is from the LiveView:


  @impl true
  def mount(_params, _session, socket) do
    {
      :ok,
      socket
      |> stream(:song_queue, [])
    }
  end

  @impl true
  def handle_info({:queue_next, %EmbeddedSong{} = song}, socket) do
    Listening.queue_song_next(socket.assigns.room, song.apple_id)

    {
      :noreply,
      socket
      |> stream_insert(:song_queue, song, at: 0)
      |> put_flash(:info, "Added '#{song.name}' to start of queue.")
    }
  end

  @impl true
  def handle_event("clear_queue", _params, socket) do
    Listening.clear_queue!(socket.assigns.room)

    {
      :noreply,
      socket
      |> stream(:song_queue, [], reset: true)
      |> put_flash(:info, "Cleared the queue.")
    }
  end

  @impl true
  def render(assigns) do
    ~H"""
    <p>Listening</p>
    <.live_component module={SearchComponent} id="search-music-component" />

    <br />
    <hr />
    <div>
      <p>Queue</p>
      <ol id="song-queue-list" phx-update="stream">
        <li :for={{_id, song} <- @streams.song_queue}>
          <%= song.name %> by <%= song.artist %>
        </li>
      </ol>
      <button phx-click="clear_queue">Clear</button>
    </div>
    """
  end

It’s because you’re throwing away the id:

<li :for={{_id, song} <- @streams.song_queue}>

Change it to:

<li :for={{dom_id, song} <- @streams.song_queue} id={dom_id}>

I don’t know the internals of streams well enough to explain it, but I do know that LV is heavily id-dependent. At least in the case of emptying the stream, my guess is that it thinks it’s already empty. In any event, adding the id should fix it.

2 Likes

Thank you so much!

That worked for clearing the queue. Unfortunately, the prepending functionality is still broken.

It’s behaving peculiarly, and replaces the last element of the stream instead of prepending to it.

Had to step away for some time to realize the error. As you said, LV is heavily id-dependent. My embedded schema wasn’t generating and maintaining an id. Once every EmbeddedSong had its own id, it worked as expected.

Thank you again!

1 Like

Glad you sorted it out!

1 Like