Using streams or lists for collections in assigns?

Hello community

I am experimenting with streams, playing with the following simple template displaying a list. One element is ‘selected’ and gets a different style.

    ~H"""
    <ul>
      <li 
        :for={elem <- @list} 
        id={elem.id} 
        class={style_selected(elem.id == @selected_id)}
      >
        <%= elem.label %>
      </li>
    </ul>
    """

Now when using assign(socket, list: list) to update the list, and assign(socket, selected_id: id), then the styling works out of the box. However, all list elements will get sent to the browser as update, even the unchanged ones.

When using stream(socket, :list, list) instead, only the updated elements will get sent to the browser. However, more magic is needed to update the styling of the selected element. The list needs to be retrieved again from cache or database, as the LiveView process intendedly does not keep these elements.

Is there a way to combine the advantage of the minimal change identification for collections while still keeping the collection available in the liveview process, for further change tracking?

Making the individual items live components moves them into a sepratately change tracked context. So the non-streamed approach would still “update” the live components (server side), but (most of) those components would notice that their inner html didn’t change and result in no changes sent to the client.

That’s a more code heavy approach though.

2 Likes

You need to insert both the deselected item and the newly selected item: socket |> stream(:list, [delected_item, selected_item]). As LostKobrakai said, you can also track stream items in LiveComponents for isolated updates, but you’d still need to toggle the previously deselected item.

1 Like

Thank you both very much for your replies and all your work!

You need to insert both the deselected item and the newly selected item: socket |> stream(:list, [delected_item, selected_item]) . As LostKobrakai said, you can also track stream items in LiveComponents for isolated updates, but you’d still need to toggle the previously deselected item.

For this, one needs to look up the changed items again, as they are not in the state of the liveview process anymore. In your TodoTrek example, there is a database lookup needed for re-inserting items to the stream in the use case of repositioning items on the board.

@chrismccord, you mentioned in your ElixirConf Keynote

Everytime you have a collection in LiveView, you probably wanna use a stream

I completely see the benefit for large collections, where server memory and full-list client updates become costly. However, for small to medium size collections, the stream implementation come with extra lookup effort (database lookup) and more code to write (implementing change tracking, which items to insert into the stream when). Do you agree the case is less clear there?