diff --git a/docs/docs/05-server-side-rendering/04-datastar.md b/docs/docs/05-server-side-rendering/04-datastar.md
index e71e96a24..e098327b7 100644
--- a/docs/docs/05-server-side-rendering/04-datastar.md
+++ b/docs/docs/05-server-side-rendering/04-datastar.md
@@ -2,7 +2,7 @@
[Datastar](https://data-star.dev) is a hypermedia framework that is similar to [HTMX](htmx).
-Datastar can be used to selectively replace content within a web page by combining fine grained reactive signals with SSE. It's geared primarily to real time applications where you'd normally reach for a SPA framework such as React/Vue/Svelte.
+Datastar can selectively replace content within a web page by combining fine-grained reactive signals with SSE. It's geared primarily to real-time applications where you'd normally reach for a SPA framework such as React/Vue/Svelte.
## Usage
@@ -13,7 +13,7 @@ Using Datastar requires:
## Installation
-Datastar comes out of the box with templ components to speed up development. You can use `@datastar.ScriptCDNLatest()` or `ScriptCDNVersion(version string)` to include the latest version of the Datastar library in your HTML.
+Datastar is included with Templ components out of the box to speed up development. You can use `@datastar.ScriptCDNLatest()` or `ScriptCDNVersion(version string)` to include the latest version of the Datastar library in your HTML.
:::info
Advanced Datastar installation and usage help is covered in the user guide at https://data-star.dev.
@@ -35,12 +35,14 @@ We are going to modify the [templ counter example](example-counter-application)
### Frontend
-First, define a HTML some with two buttons. One to update a global state, and one to update a per-user state.
+First, define some HTML with two buttons. One to update a global state, and one to update a per-user state.
```templ title="components.templ"
package site
-type TemplCounterStore struct {
+import datastar "github.com/starfederation/datastar/sdk/go"
+
+type TemplCounterSignals struct {
Global uint32 `json:"global"`
User uint32 `json:"user"`
}
@@ -48,12 +50,13 @@ type TemplCounterStore struct {
templ templCounterExampleButtons() {
@templCounterExampleButtons()
@templCounterExampleCounts()
@@ -85,13 +88,13 @@ templ templCounterExampleInitialContents(store TemplCounterStore) {
```
:::tip
-Note that Datastar doesn't promote the use of forms, because they are ill-suited to nested reactive contents. Instead it sends all[^1] reactive state (as JSON) to the server on each request. This means far less bookkeeping and more predictable state management.
+Note that Datastar doesn't promote the use of forms because they are ill-suited to nested reactive content. Instead, it sends all[^1] reactive state (as JSON) to the server on each request. This means far less bookkeeping and more predictable state management.
:::
:::note
-`data-store` is a special attribute that Datastar uses to store the initial state of the page. The contents will turn into signals that can be used to update the page.
+`data-signals` is a special attribute that Datastar uses to merge one or more signals into the existing signals. In the example, we store $global and $user when we initially render the container.
-`data-on-click="$$post('/examples/templ_counter/increment/global')"` is an attribute expression that says "when this element is clicked, send a POST request to the server to the specified URL". The `$$post` is an action that is a sandboxed function that knows about things like signals.
+`data-on-click="@post('/examples/templ_counter/increment/global')"` is an attribute expression that says "When this element is clicked, send a POST request to the server to the specified URL". The `@post` is an action that is a sandboxed function that knows about things like signals.
`data-text="$global"` is an attribute expression that says "replace the contents of this element with the value of the `global` signal in the store". This is a reactive signal that will update the page when the value changes, which we'll see in a moment.
:::
@@ -108,12 +111,13 @@ import (
"sync/atomic"
"github.com/Jeffail/gabs/v2"
- "github.com/delaneyj/datastar"
"github.com/go-chi/chi/v5"
"github.com/gorilla/sessions"
+ datastar "github.com/starfederation/datastar/sdk/go"
)
-func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.Store) error {
+func setupExamplesTemplCounter(examplesRouter chi.Router, sessionSignals sessions.Store) error {
+
var globalCounter atomic.Uint32
const (
sessionKey = "templ_counter"
@@ -121,7 +125,7 @@ func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.
)
userVal := func(r *http.Request) (uint32, *sessions.Session, error) {
- sess, err := sessionStore.Get(r, sessionKey)
+ sess, err := sessionSignals.Get(r, sessionKey)
if err != nil {
return 0, nil, err
}
@@ -139,17 +143,17 @@ func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.
http.Error(w, err.Error(), http.StatusInternalServerError)
}
- store := TemplCounterStore{
+ signals := TemplCounterSignals{
Global: globalCounter.Load(),
User: userVal,
}
- sse := datastar.NewSSE(w, r)
- datastar.RenderFragmentTempl(sse, templCounterExampleInitialContents(store))
+ c := templCounterExampleInitialContents(signals)
+ datastar.NewSSE(w, r).MergeFragmentTempl(c)
})
- updateGlobal := func(store *gabs.Container) {
- store.Set(globalCounter.Add(1), "global")
+ updateGlobal := func(signals *gabs.Container) {
+ signals.Set(globalCounter.Add(1), "global")
}
examplesRouter.Route("/templ_counter/increment", func(incrementRouter chi.Router) {
@@ -157,8 +161,7 @@ func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.
update := gabs.New()
updateGlobal(update)
- sse := datastar.NewSSE(w, r)
- datastar.PatchStore(sse, update)
+ datastar.NewSSE(w, r).MarshalAndMergeSignals(update)
})
incrementRouter.Post("/user", func(w http.ResponseWriter, r *http.Request) {
@@ -177,30 +180,28 @@ func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.
updateGlobal(update)
update.Set(val, "user")
- sse := datastar.NewSSE(w, r)
- datastar.PatchStore(sse, update)
+ datastar.NewSSE(w, r).MarshalAndMergeSignals(update)
})
})
return nil
-}
-```
+}```
-The `atomic.Uint32` type is used to store the global state. The `userVal` function is a helper that retrieves the user's session state. The `updateGlobal` function increments the global state.
+The `atomic.Uint32` type stores the global state. The `userVal` function is a helper that retrieves the user's session state. The `updateGlobal` function increments the global state.
:::note
-In this example, the global state is stored in RAM, and will be lost when the web server reboots. To support load-balanced web servers, and stateless function deployments, consider storing the state in a data store such as [NATS KV](https://docs.nats.io/using-nats/developer/develop_jetstream/kv).
+In this example, the global state is stored in RAM and will be lost when the web server reboots. To support load-balanced web servers and stateless function deployments, consider storing the state in a data store such as [NATS KV](https://docs.nats.io/using-nats/developer/develop_jetstream/kv).
:::
### Per-user session state
-In a HTTP application, per-user state information is partitioned by a HTTP cookie. Cookies that identify a user while they're using a site are known as "session cookies". When the HTTP handler receives a request, it can read the session ID of the user from the cookie and retrieve any required state.
+In an HTTP application, per-user state information is partitioned by an HTTP cookie. Cookies that identify a user while they're using a site are known as "session cookies". When the HTTP handler receives a request, it can read the session ID of the user from the cookie and retrieve any required state.
-### Signal only patching
+### Signal-only patching
-Since the page's elements aren't changing dynamically, we can use the `datastar.PatchStore` function to send only the signals that have changed. This is a more efficient way to update the page without even needing to send HTML fragments.
+Since the page's elements aren't changing dynamically, we can use the `MarshalAndMergeSignals` function to send only the signals that have changed. This is a more efficient way to update the page without even needing to send HTML fragments.
:::tip
-Datastar will merge updates to the store similar to a JSON merge patch. This means you can do dynamic partial updates to the store and the page will update accordingly. [Gabs](https://pkg.go.dev/github.com/Jeffail/gabs/v2#section-readme) is used here to handle dynamic JSON in Go.
+Datastar will merge updates to signals similar to a JSON merge patch. This means you can do dynamic partial updates to the store and the page will update accordingly. [Gabs](https://pkg.go.dev/github.com/Jeffail/gabs/v2#section-readme) is used here to handle dynamic JSON in Go.
-[^1]: You can control the data that is sent to the server by prefixing local signals with `_`. This will prevent them from being sent to the server on every request.
+[^1]: You can control the data sent to the server by prefixing local signals with `_`. This will prevent them from being sent to the server on every request.