From 7503e1b2c146585950a2972b22b81a93561fcc55 Mon Sep 17 00:00:00 2001 From: maddalax Date: Fri, 4 Oct 2024 11:12:38 -0500 Subject: [PATCH] add docs for SSE --- examples/chat/pages/chat.$id.go | 9 +-- .../6_pushing_data/1_server_sent_events.md | 65 +++++++++++++++++++ htmgo-site/pages/examples.go | 11 ++-- 3 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 htmgo-site/md/docs/6_pushing_data/1_server_sent_events.md diff --git a/examples/chat/pages/chat.$id.go b/examples/chat/pages/chat.$id.go index 1b71761..adabbb7 100644 --- a/examples/chat/pages/chat.$id.go +++ b/examples/chat/pages/chat.$id.go @@ -16,10 +16,7 @@ func ChatRoom(ctx *h.RequestContext) *h.Page { return h.NewPage( RootPage( h.Div( - h.JoinExtensions( - h.TriggerChildren(), - h.HxExtension("ws"), - ), + h.TriggerChildren(), h.Attribute("sse-connect", fmt.Sprintf("/ws/chat/%s", roomId)), @@ -143,10 +140,6 @@ func MessageInput() *h.Element { return h.Input("text", h.Id("message-input"), h.Required(), - h.Class("p-4 rounded-md border border-slate-200 w-full focus:outline-none focus:ring focus:ring-slate-200"), - h.Name("message"), - h.MaxLength(1000), - h.Placeholder("Type a message..."), h.HxAfterSseMessage( js.SetValue(""), ), diff --git a/htmgo-site/md/docs/6_pushing_data/1_server_sent_events.md b/htmgo-site/md/docs/6_pushing_data/1_server_sent_events.md new file mode 100644 index 0000000..2e64f89 --- /dev/null +++ b/htmgo-site/md/docs/6_pushing_data/1_server_sent_events.md @@ -0,0 +1,65 @@ +**Server Sent Events (SSE)** + +htmgo supports server-sent events (SSE) out of the box. +This allows you to push data from the server to the client in real-time. + +Example of this can be found in the [chat-app](https://github.com/maddalax/htmgo/tree/master/examples/chat) example. +Demo: https://chat-example.htmgo.dev + +## How it works ## +1. The client sends a request to the server to establish a connection. +2. The server holds the connection open and sends data (in our case, most likely elements) to the client whenever there is new data to send. +3. The htmgo SSE extension uses https://htmx.org/attributes/hx-swap-oob/ to swap out the elements that the server sends. + + +**Note**: SSE is **unidirectional** (the server can only send data to the client). +For the client to send data to the server, normal xhr behavior should be used (form submission, triggers, etc). + +## Usage +1. Add the SSE connection attribute and the path to the handler that will handle the connection. + +```go +h.Attribute("sse-connect", fmt.Sprintf("/chat/%s", roomId)) +``` + +The following **Event Handlers** can be used to react to SSE connections. +```go +h.HxOnSseOpen +h.HxBeforeSseMessage +h.HxAfterSseMessage +h.HxOnSseError +h.HxOnSseClose +h.HxOnSseConnecting +``` + +**Example:** Adding an event listener handle SSE errors. + +```go +h.HxOnSseError( + js.EvalJs(fmt.Sprintf(` + const reason = e.detail.event.data + if(['invalid room', 'no session', 'invalid user'].includes(reason)) { + window.location.href = '/?roomId=%s'; + } else if(e.detail.event.code === 1011) { + window.location.reload() + } else if (e.detail.event.code === 1008 || e.detail.event.code === 1006) { + window.location.href = '/?roomId=%s'; + } else { + console.error('Connection closed:', e.detail.event) + } + `, roomId, roomId)), +), +``` + +**Example:** Clearing the input field after sending a message. +```go +func MessageInput() *h.Element { + return h.Input("text", + h.Id("message-input"), + h.Required(), + h.HxAfterSseMessage( + js.SetValue(""), + ), + ) +} +``` diff --git a/htmgo-site/pages/examples.go b/htmgo-site/pages/examples.go index 3db313a..73aaf2b 100644 --- a/htmgo-site/pages/examples.go +++ b/htmgo-site/pages/examples.go @@ -15,10 +15,11 @@ type Example struct { var examples = []Example{ { - Title: "Chat App Example", - Github: "https://github.com/maddalax/htmgo/tree/master/examples/chat", - Demo: "https://chat-example.htmgo.dev", - Image: "public/chat-example.jpg", + Title: "Chat App Example", + Github: "https://github.com/maddalax/htmgo/tree/master/examples/chat", + Description: "A simple chat application built with htmgo using SSE for real-time updates", + Demo: "https://chat-example.htmgo.dev", + Image: "public/chat-example.jpg", }, { Title: "Todo List MVC", @@ -73,7 +74,7 @@ func ExamplesPage(ctx *h.RequestContext) *h.Page { func ExampleCards() *h.Element { return h.Div( - h.Class("prose-h2:my-1 prose-img:my-1 grid grid-cols-1 md:grid-cols-3 gap-6 text-center pb-8"), // Using grid for 3-column layout + h.Class("prose-h2:my-1 prose-img:my-1 grid grid-cols-1 md:grid-cols-2 gap-6 text-center pb-8"), // Using grid for 3-column layout h.List(examples, func(example Example, index int) *h.Element { return h.Div( h.Class("border border-gray-200 shadow-sm rounded-md px-4 pb-4 bg-neutral-100"), // Removed specific width, handled by grid