Per-element actions for elements in LiveView stream

Hi all

My go to pattern for handling data recently has been: have a Delete button on item. When user clicks the button, overlay the item with “Are you sure Yes/No”, and only delete the item when the user clicks “Yes”.

For non-streams this is easy.

socket
|> assign(a_list_of_items: items)
|> assign(requested_for_removal: nil)


# .... and then in a handle_event

socket |> assign(requested_for_removal: selected_item_id)

# .... and in heex

<div :for={ item <- @items}>
   <div :if={ item.id != @requested_for_removal }>item data</div>
   <div :if={ item.id == @requested_for_removal}>Are you sure? etc.</div>
</div>

For streams, however, this presents a bit of a problem. Since items are not retained in memory, I need to:

  • fetch the item when delete is clicked
  • find its index in the stream
  • update the stream at that position with data like “requested for removal”
  • and if, for example, I want to only have one such overlay on page, I need to somehow remove it from a previous element

Has someone implemented something similar, and is there a better way?

Perhaps render hidden divs and show them with JS by toggling class names on elements?

Yes, you could do it the way you suggest - have a hidden deletion element and toggle between the two.

1 Like

This shouldn’t be necessary, because the stream helpers can update items in-place based on the item ID.

You could use that to update the state of the single stream entry with a deletion_requested: true, causing the template for the item to re-render and a minimal diff be sent to the client.

In fact this pattern would allow multiple items to be in this pending state simultaneously.

This also reminds me that probably you wouldn’t want to accidently re-add items to a stream during that event handler, or perhaps think about what’s the desired behavior - see stream_insert update_only aka stream_update by SteffenDE · Pull Request #3573 · phoenixframework/phoenix_live_view · GitHub & https://elixirforum.com/t/add-stream-update-to-liveview-streams/68107

However, in order to update the state of that entry I need to retrieve the full entry, and set deletion_requested: true on it :slight_smile:

So far I’ve gone the JS route with a hidden element.