Skip to content

Instantly share code, notes, and snippets.

@1cg
Last active September 15, 2020 18:37
Show Gist options
  • Save 1cg/5dd26530aeec92a9389b367747bbb5ee to your computer and use it in GitHub Desktop.
Save 1cg/5dd26530aeec92a9389b367747bbb5ee to your computer and use it in GitHub Desktop.
HTMX SSE Swap Proposal

HTMX SSE Improvements Doc

Overview

The current SSE implementation is geared towards triggering events in the DOM, requiring an HTTP request to be sent up to get new content:

  <div hx-sse="connect /event_stream">
    <div hx-get="/chatroom" hx-trigger="sse:chatter">
      ...
    </div>
  </div>

In this case, an SSE event named chatter would trigger a GET to the /chatroom URL.

This is a very limited use of the Server Sent Events API. In particular, there are two aspects of SSE that are ignored:

  1. SSE events can have data (not just names) associated with them
  2. SSE events do not need to have a name (i.e. they can be pure data-only messages)

I propose the following extensions to the current SSE functionality:

Swapping on Named Events

If an element wants to directly swap the data of a message into its body, I propose the following syntax:

  <div hx-sse="connect /event_stream">
    <div hx-sse="swap on chatter">
      ...
    </div>
  </div>

The swap on indicates that this element wants to listen for chatter messages and swap the body when they occur.

The hx-swap attribute can then be used to determine exactly how the swap is done, as with normal swaps.

Data-only Messages

In the case of a data-only message, the message type is message according to the SSE spec. Thus, the form swap on message should work the same as the named event implementation.

  <div hx-sse="connect /event_stream">
    <div hx-sse="swap on message">
      ...
    </div>
  </div>

Declaring and Using An SSE Endpoint On The Same Element

We should support both connecting and listening for an event (or events) in the same hx-sse declaration, by allowing comma separated values:

  <div hx-sse="connect /event_stream, swap on message">
     ...
  </div>

Summary

These two changes would be relatively modest, but I think would address the major shortcomings of the current SSE behavior without requiring a large code refactor. With these changes SSE is on equal, or perhaps greater footing than WebSockets as a way to achieve dynamic web UI within the declarative model.

Another nice aspect of this proposal is that the features are additive, which means that SSE improvments, which I very much wnat to see implemented, would not be a blocker to an htmx 1.0 release.

Links

  1. MDN SSE Article - https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
  2. SSE Spec - https://html.spec.whatwg.org/multipage/server-sent-events.html
  3. Initial SSE improvement PR - bigskysoftware/htmx#155
  4. Second SSE improvement PR - bigskysoftware/htmx#185
@1cg
Copy link
Author

1cg commented Sep 14, 2020

we could go full hyperscript and split on and :)

connect https://blah.blah?whatever and swap on message

@clarkevans
Copy link

clarkevans commented Sep 14, 2020

Sure. I think and is fine. Alternatively, one could use double quotes, but this requires you use single quotes for the attribute... <div hx-swap='connect "my-url-with-commas" swap Event1,Event2' /> -- it's ugly. "connect <https://blah.blah?whatever,comma>; swap on message" works, but, frankly, I like and ...

@benpate
Copy link

benpate commented Sep 14, 2020

Hey all.. I agree on NOT making a full Lexer. There was yet another problem with PR #193, so I'm putting together a correct one with the existing syntax while we consider and discuss commas. It will be easy to switch it out for whatever final form is decided.

If we're voting, I still like hx-swap="connect my-url-with-commas swap:eventname" best, because it seems consistent with the way that other tags already work (like hx-swap)

@clarkevans
Copy link

I do want to thank you both for working though this. I don't expect to use htmx for a few more months, but it's really encouraging to see the thoughtful work going into it. So, I'm OK with "swap:eventname" as well.

@benpate
Copy link

benpate commented Sep 14, 2020

Apparently, I may have figured out how to make a clean PR, because I just posted PR#194 and it hasn't blown up, yet. Please check my work to see if it looks good?

@1cg
Copy link
Author

1cg commented Sep 14, 2020

What about:

hx-sse="connect:https://my-url-with-commas swap:eventname"

so no whitespace between the connect and the URL. That would allow us to split on whitespace.

We could update the websockets to be similar.

@clarkevans
Copy link

clarkevans commented Sep 14, 2020

Sure, verb:args works. Do your verbs, connect and swap always have a known number of arguments? In which case, "connect https://... swap eventname" work fine. You split on space. Then process the stack from the front. connect would eat the next item on the stack, https://my-url-with-commas and return; swap could then be triggered next, which could eat eventname, etc. There's also nothing wrong with and, this provides you the ability to have variable number of verb arguments (provided they arn't the and keyword).

@benpate
Copy link

benpate commented Sep 14, 2020

@1cg - I like the syntax you’re suggesting. Although the double colon looks a little odd, in most cases a page would be calling back to a relative path on its own server, so it’s not an issue. We’d need to be a little careful with how we parse double colons (so we don’t drop content after the (https:”), but there are already examples of this in the library, so it should be simple enough to match.

I’ll let you think on it for a bit. Just say “go” and I’ll update this PR.

@clarkevans - yes. At this time, all of the keywords have only one argument. Think of them more as named parameters, or key/value pairs. We should be able to maintain this convention, if we just pick a new keyword for any additional arguments that we add in the future.

@1cg
Copy link
Author

1cg commented Sep 15, 2020

I have pushed up changes so that we now use whitespace as the declaration separator and the first colon as the argument delimiter:

<div hx-sse="connect:/foo swap:bar">...</div>

Same for both SSE and web sockets.

I also looked at combining the swap code for at least web sockets and SSE, but they acted differently enough that I didn't want to do it at this point. Web Sockets are a pure out of band situation, since there isn't a way to signal which messages you are interested in, so it just acts differently. And when I looked at pulling out the AJAX swap, there was a large number of parameters required.

I'll take another look at it when I have a bit more time to think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment