Question with heex code when updating the version of liveview

Hello.
My application requires displaying information about nodes and the cells they contain. The web page content is generated dynamically depending on the content, for example:
Node 1
Cell 1.2 Cell 1.45
Node 3
Cell 3.2

and so on.
In Phoenix 1.7.21 with phoenix_live_view 0.20.17 it worked fine:


But in new versions, for example Phoenix 1.8.0 with phoenix_live_view 1.1.3 , it broke:

Here is an example of code from the heex file:

<div id="cells" class="w-[96rem] text-left my-3 grid grid-cols-2 gap-1" phx-update="stream">
  <%= for {dom_id, cell} <- @streams.cells do %>
    <%= if cell.first do %>
      <div class="px-1 col-start-1 col-end-3" id={"#{cell.node}"}>
        <div class="font-semibold hover:text-sky-500">
          <%= gettext("Node") %>&nbsp;<%= "#{cell.node}" %>
        </div>
      </div>
    <% end %>
    <div class="container grid grid-rows-2 grid-cols-9 border border-gray-400 rounded-lg overflow-hidden" id={"#{dom_id}"}>
      <div class="font-semibold leading-tight py-1 px-1 row-start-1 row-end-3 col-start-1 col-end-3 border-solid border-r border-gray-400">
        <%= gettext("Cell") %>&nbsp;<%= cell.node %>.<%= cell.id %>
      </div>
    </div>
  <% end %>
</div>

Here is the content of the variable “cells”:

cells = [
  %{id: 408, first: true, node: 15},
  %{id: 12, first: false, node: 15},
  %{id: 11, first: true, node: 800},
  %{id: 14, first: false, node: 800},
   %{id: 1, first: true, node: 900}
]

I tried to insert the console output into the text of the heex file and I see that the data comes in the correct order.
It is clear that before connecting the socket, the HTML text is formed correctly, but then it is not displayed in the order I expect.
Maybe someone can tell me how to fix this?

Your stream content is not formed properly. The stream children should all have the dom_id. In your case some of the children have it, but some (those wrapped in the if cell.first block) do not.

I don’t think there is documented behavior for rendering non-stream items in streams. This code was substantially refactored in LV1.1, so the (undocumented) behavior may have changed.

Each iteration of your for comprehension should render one child with id={dom_id}, so you should restructure your code to do that.

<div phx-update="stream">
  <%= for {dom_id, cell} <- @streams.cells do %>
    <div id={dom_id}>
      <%= if cell.first do %>
        <div>{cell.node}</div>
      <% end %>
      <div>{cell.id}</div>
    </div>
  <% end %>
</div>

Alternatively, consider ditching streams and just using a for comprehension over a list of cells stored in a normal assign. For comprehensions are now efficiently diffed if you pass a :key, so there is little reason to use streams unless you absolutely must minimize memory usage on the server (if you have a huge number of “cells”, whatever they are).

The for comprehension approach also has the advantage of being much less confusing (case in point).