Can we use Phoenix LiveView Stream more than one time in the View

I’ve just trying out LiveView for the first time, and I’ve generated a live endpoint with mix phx.gen.live TodoService Todo todos ..., I have pulled in the generated routes to my routes file and it’s working.

Just to test things out, I tried rendering some HTML by editing the generated Heex, and it’s not working.

...
...
<ul
  id="todos_list"
  phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}
>
  <li
    :for={{dom_id, todo} <- @streams.todos}
    id={dom_id}
  >
    <span><%= todo.name %></span>
  </li>
</ul>

<.table
  id="todos"
  rows={@streams.todos}
  row_click={fn {_id, todo} -> JS.navigate(~p"/todos/#{todo}") end}
>
  <:col :let={{_id, todo}} label="Name"><%= todo.name %></:col>
  <:col :let={{_id, todo}} label="Notes"><%= todo.notes %></:col>
...
...

I’m getting the below error:

KeyError at GET /todos

`key :rows not found in: %{
  socket: #Phoenix.LiveView.Socket<
    id: "phx-F70FhIK4AoARkQvC",
    endpoint: TodoAppWeb.Endpoint,
    view: TodoAppWeb.TodoLive.Index,
    parent_pid: nil,
    root_pid: nil,
    router: TodoAppWeb.Router,
    assigns: #Phoenix.LiveView.Socket.AssignsNotInSocket<>,
    transport_pid: nil,
    ...
  >,
  __changed__: %{page_title: true, streams: true, todo: true},
  flash: %{},
  page_title: "Listing Todos",
  live_action: :index,
  streams: %{
    __changed__: MapSet.new([:todos]),
    todos: %Phoenix.LiveView.LiveStream{
      name: :todos,
      dom_id: #Function<3.112696910/1 in Phoenix.LiveView.LiveStream.new/4>,
      ref: "0",
      inserts: [
        {"todos-3", -1,
         %TodoApp.TodoService.Todo{
           __meta__: #Ecto.Schema.Metadata<:loaded, "todos">,
           id: 3,
           name: "one",
           notes: "one none",
           is_completed: false,
           is_archived: false,
           inserted_at: ~U[2024-03-15 18:53:37Z],
           updated_at: ~U[2024-03-15 18:53:37Z]
         }, nil}
      ],
      deletes: [],
      reset?: false,
      consumable?: false
    },
    __configured__: %{},
    __ref__: 1
  },
  todo: nil
}`

Guess I can’t subscribe or use a stream more than twice in a View??

Can someone elaborate on how it works. The docs is silent on streams.

Hello and welcome to Elixir Forum!

The error is saying there is no :rows assign. This is most likely your problem:

phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}

You probably mean @streams.todos instead of @rows.

2 Likes

Yes, I found it and rectified it.

Btw, can you point me to any docs or posts about LiveStream?

I’m guessing you copied that line from the the table component?

The only documentation I’m aware of for streams is for the functions themselves. %Phoenix.LiveView.LiveStream{} is simply the struct that holds data about each stream and AFAIK is considered opaque, ie, you aren’t supposed to manipulate it directly. It is what you get when you say @streams.my_stream, though.

This line from the table component:

phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}

is just doing some type checking to see if you passed a stream or not. This is so it can support both streams and a simple list. It’s making use of the fact that passing nil or false to a component attribute will cause the attribute itself not to render at all, so if match?(%Phoenix.LiveView.LiveStream{}, @rows) returns false, it’ll get phx-update={false} otherwise if it’s a stream it will get phx-update="stream".

Sorry if I’m not explaining this very well.

1 Like

is liveview streams the default or replacement for temporary assigns? if so, is there a chance they’ll add a portion of guide for streams?

Thanks, I’ll read the module docs as is. Sad, I can’t find any user written blog or article on this.

They are still relatively new and even had some problems until fairly recently. My guess is that a guide will appear in hexdocs sometime soon. I was actually surprised there wasn’t one already. If I wasn’t scrambling to find a job I would maybe take a stab at getting one started.