Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/sse/sse.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,31 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
* @param {HTMLElement} elt
*/
function registerSSE(elt) {
// Add message handlers for every `sse-swap` attribute
if (api.getAttributeValue(elt, 'sse-swap')) {
var sseSwapAttr = api.getAttributeValue(elt, 'sse-swap');
var sseConnectAttr = api.getAttributeValue(elt, 'sse-connect');

var hasSseSwap = sseSwapAttr !== null;
var hasSseConnect = sseConnectAttr !== null;
var hasNestedSseSwap = elt.querySelector('[sse-swap], [data-sse-swap]') !== null;

if (hasSseConnect && !(hasSseSwap || hasNestedSseSwap)) {
// Default `sse-swap` to `message`
sseSwapAttr = 'message';
}

// Add message handlers for every `sse-swap` attribute
if (sseSwapAttr) {
// Find closest existing event source
var sourceElement = api.getClosestMatch(elt, hasEventSource)
if (sourceElement == null) {
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
return null // no eventsource in parentage, orphaned element
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
return null // no eventsource in parentage, orphaned element
}

// Set internalData and source
var internalData = api.getInternalData(sourceElement)
var source = internalData.sseEventSource

var sseSwapAttr = api.getAttributeValue(elt, 'sse-swap')
var sseEventNames = sseSwapAttr.split(',')

for (var i = 0; i < sseEventNames.length; i++) {
Expand Down
41 changes: 41 additions & 0 deletions src/sse/test/ext/sse.js
Original file line number Diff line number Diff line change
Expand Up @@ -702,4 +702,45 @@ describe('sse extension', function() {
byId('d1').innerHTML.should.equal('div1 updated')
byId('d2').innerHTML.should.equal('div2 updated')
})

it('defaults to message event if sse-swap is not specified', function() {
var div = make('<div id="d1" hx-ext="sse" sse-connect="/event_stream"></div>');
this.clock.tick(1);
byId('d1').innerText.should.equal('');
this.eventSource.sendEvent('message', 'Event 1');
byId('d1').innerText.should.equal('Event 1');
});

it('does not default to message when parent has nested sse-swap elements', function() {
var div = make('<div id="parent" hx-ext="sse" sse-connect="/events">\n' +
' <div id="foo" sse-swap="foo"></div>\n' +
' <div id="bar" sse-swap="bar"></div>\n' +
'</div>');
this.clock.tick(1);

// Parent should NOT have default message event listener when it has nested sse-swap children
(this.eventSource._listeners.message == undefined).should.be.true;

// Child elements should have their specific event listeners
this.eventSource._listeners.foo.should.be.lengthOf(1);
this.eventSource._listeners.bar.should.be.lengthOf(1);

// Test that each element responds to its specific event
byId('foo').innerText.should.equal('');
byId('bar').innerText.should.equal('');

this.eventSource.sendEvent('foo', 'Foo Event');
byId('foo').innerText.should.equal('Foo Event');
byId('bar').innerText.should.equal('');

this.eventSource.sendEvent('bar', 'Bar Event');
byId('foo').innerText.should.equal('Foo Event');
byId('bar').innerText.should.equal('Bar Event');

// Message events should not affect anything since parent doesn't listen for them
this.eventSource.sendEvent('message', 'Default Message');
byId('foo').innerText.should.equal('Foo Event');
byId('bar').innerText.should.equal('Bar Event');
byId('parent').innerText.should.not.contain('Default Message');
});
})