SSE extension doesn't take into account children that are added after initialisation
The SSE extension add listeners to children with sse-swap tags when the EventSource is created.
If, for any reason, a node is added later with its own sse-swap, the event will be ignored. Same if an existing node is replaced with another with same sse-swap.
As an example, let's say that I have a list of items and I want to change the one marked as "current" (via a class), so I use `hx-swap='outerHTML' for this:
<ul hx-ext="sse" sse-connect="/items" hx-swap="outerHTML">
<li sse-swap="Item1" class="current">1</li>
<li sse-swap="Item2">2</li>
<li sse-swap="Item3">3</li>
</ul>
If I send these events:
event: Item1
data: <li sse-swap="Item1">1</li>
event: Item2
data: <li sse-swap="Item2" class="current">2</li>
It will work the first time, ie the <li> will be replaced, but not the next times as the list of children is not updated, so the only event that will work, until sent once, will be Item3.
There is a lot of things that can impact the children, like a form adding a new element. Reloading the whole list is not a valid solution (the list can be long). Same for adding, in the example, a "container" node in the <li> (and removing the hx-swap="outerHTML") because then I won't be able to change the classes or other attributes of the <li>.
@twidi just a small question: what do you use server-side as SSE implementation? (I suppose you are using Django)
Also seeing the same thing. In fact, the SSE extension maintains a reference to the (now swapped out) original node instead of releasing it (thus resulting in a small memory leak).
Example dom:
<div hx-ext="sse" sse-connect="/counter">
<div hx-get="/identity">
This will be swapped out on click with the same DOM
<div sse-swap="counter">
Seconds passed: 0 <!-- update every second -->
</div>
</div>
</div>
After clicking the div, the counter will cease to work.
Just ran into this one ... and here is a quick and dirty workaround that Im using.
I have a global SSE connection that applies to the body, and a header that updates itself whenever an 'update' event is seen.
<body hx-ext="sse" sse-connect="/events">
<div class="header" hx-get="/header" hx-trigger="load, sse:update"></div>
... rest of the page here
</body>
So far so good !
Now, in some contexts the header contents may include elements that also want to listen in on events on the same SSE source.
For example this clock element. Intuitively, this should work, as its a child of the <body> element which has the sse-connect defined, but it doesnt because of the reasoning above from the OP.
<span class="clock" sse-swap="clock">
.. this never updates when a clock event is received ..
</span>
Simple workaround is to apply the ext and the source to the element again. Looking through the code in ext/sse.js, it appears to be safe enough, as htmx will re-use the same event source if it finds one connected to another element.
<span class="clock" hx-ext="sse" sse-connect="/events" sse-swap="clock">
.. this correctly updates when a clock event is received ..
</span>
I dont know if there are any negative side effects on the client end from doing this workaround - but the main thing (for me), is that it doesnt end up spamming my server with multiple connections to the /events endpoint, so Im OK with that.
sadly I cannot confirm the above workaround - my server is being spammed by multiple SSE requests.
I think the main question here is: can the extensions API even support this use case? Imho ideally there'd be a way for extensions to "initialize" nodes no matter whether they're present in the original response or in some swapped in node
it seems to me that it would be easiest to
A.) keep a dictionary of url <- EventSource instead of simply creating a new event source and
B.) to retrieve the sse url not from the element directly but rather to look up the hierarchy for the nearest sse-connect value.
I'll see if I can create a PR tomorrow
Just ran into the same issue. I use a slight variation on @twidi's example, as I also listen to an event on the element on which hx-ext=sse is defined:
<ul hx-ext="sse" sse-connect="/items" sse-swap="ListEvent", hx-swap="outerHTML">
<li sse-swap="Item1" class="current">1</li>
<li sse-swap="Item2">2</li>
<li sse-swap="Item3">3</li>
</ul>
but the problem seems to be the same.
One remark: if I swap the entire ul using the ListEvent, the li elements can successfully listen to their respective events. That also causes a new call to /items and forces me to send the entire list again, so while this works, it is not great.
The workaround from @zigster64 did not work for me, unfortunately.
Hi I have the same problem.
The workaround from @zigster64 does work, but it does duplicate the connection which isn't really what you want.
since my PR got closed due to being a draft (no consensus on how to test) I would ask the participants in this thread to try using my forked sse extension and maybe report problems on the (closed) PR? Maybe a solution can be found that will be accepted.
I can confirm this is no longer an issue with 1.9.11.
I can have multiple updates from the same sse-swap element.