Add load more how to [deploy-site]

This commit is contained in:
Ben Croker 2025-03-05 19:53:43 -06:00
parent d60f326230
commit bebd836f45
No known key found for this signature in database
GPG Key ID: 09D799816F1CF332
8 changed files with 181 additions and 4 deletions

View File

@ -23,6 +23,7 @@ func setupHowTos(ctx context.Context, router chi.Router) error {
Label: "How Tos",
Links: []*SidebarLink{
{ID: "how_to_bind_keydown_events_to_specific_keys"},
{ID: "how_to_load_more_list_items"},
{ID: "how_to_poll_the_backend_at_regular_intervals"},
{ID: "how_to_redirect_the_page_from_the_backend"},
},
@ -76,8 +77,9 @@ func setupHowTos(ctx context.Context, router chi.Router) error {
})
if err := errors.Join(
setupHowTosLoadMore(howTosRouter),
setupHowTosPolling(howTosRouter),
setupHowTosRedirects(howTosRouter),
setupHowTosRedirect(howTosRouter),
); err != nil {
panic(fmt.Sprintf("error setting up examples routes: %s", err))
}

View File

@ -0,0 +1,47 @@
package site
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
datastar "github.com/starfederation/datastar/sdk/go"
)
type OffsetSignals struct {
Offset int `json:"offset"`
}
func setupHowTosLoadMore(howTosRedirect chi.Router) error {
howTosRedirect.Route("/load_more/data", func(dataRouter chi.Router) {
dataRouter.Get("/", func(w http.ResponseWriter, r *http.Request) {
signals := &OffsetSignals{}
if err := datastar.ReadSignals(r, signals); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
max := 5
limit := 1
offset := signals.Offset
sse := datastar.NewSSE(w, r)
if offset < max {
newOffset := offset + limit
sse.MergeFragments(fmt.Sprintf(`<div class="text-primary font-bold">Item %d</div>`, newOffset),
datastar.WithSelectorID("list"),
datastar.WithMergeMode(datastar.FragmentMergeModeAppend),
)
if newOffset < max {
sse.MergeSignals([]byte(fmt.Sprintf(`{offset: %d}`, newOffset)))
} else {
sse.RemoveFragments(`#load-more`)
}
}
})
})
return nil
}

View File

@ -8,7 +8,7 @@ import (
datastar "github.com/starfederation/datastar/sdk/go"
)
func setupHowTosRedirects(howTosRedirect chi.Router) error {
func setupHowTosRedirect(howTosRedirect chi.Router) error {
howTosRedirect.Route("/redirect/data", func(dataRouter chi.Router) {

View File

@ -0,0 +1,35 @@
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
datastar "github.com/starfederation/datastar/sdk/go"
)
type OffsetSignals struct {
Offset int `json:"offset"`
}
signals := &OffsetSignals{}
if err := datastar.ReadSignals(r, signals); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
max := 5
limit := 1
offset := signals.Offset
sse := datastar.NewSSE(w, r)
if offset < max {
newOffset := offset + limit
sse.MergeFragments(fmt.Sprintf(`<div>Item %d</div>`, newOffset),
datastar.WithSelectorID("list"),
datastar.WithMergeMode(datastar.FragmentMergeModeAppend),
)
if newOffset < max {
sse.MergeSignals([]byte(fmt.Sprintf(`{offset: %d}`, newOffset)))
} else {
sse.RemoveFragments(`#load-more`)
}
}

View File

@ -0,0 +1,23 @@
use starfederation\datastar\enums\FragmentMergeMode;
use starfederation\datastar\ServerSentEventGenerator;
$signals = ServerSentEventGenerator::readSignals();
$max = 5;
$limit = 1;
$offset = $signals['offset'] ?? 1;
$sse = new ServerSentEventGenerator();
if ($offset < $max) {
$newOffset = $offset + $limit;
$sse->mergeFragments(`<div>Item $newOffset</div>`, [
'selector' => '#list',
'mergeMode' => FragmentMergeMode::Append,
]);
if (newOffset < $max) {
$sse->mergeSignals(['offset' => $newOffset]);
} else {
$sse->removeFragments('#load-more');
}
}

View File

@ -0,0 +1,70 @@
# How to load more list items
## Intro
Loading more list items into the DOM from the backend is a common alternative to pagination. What's makes it different is that we need to append the new items to the existing list, rather than replace them.
## Goal
Our goal is to incrementally append list items into a specific part of the DOM, each time a button is clicked. Once five items are visible, the button should be removed.
## Demo
<div id="list" data-signals-offset="1">
<div class="text-primary font-bold">Item 1</div>
</div>
<div>
<button id="load-more" data-on-click="@get('/how_tos/load_more/data')" class="btn btn-primary font-bold">
Click to load another item
</button>
</div>
## Steps
We'll give the list item container and the button unique IDs, so that we can target them individually.
We'll use a `data-signals-*` attribute to set the initial `offset` to `1`, and a `data-on-click` button that will send a `GET` request to the backend.
```html
<div id="list">
<div>Item 1</div>
</div>
<button id="load-more"
data-signals-offset="1"
data-on-click="@get('/how_tos/load_more/data')">
Click to load another item
</button>
```
The backend will receive the `offset` signal and, if not above the max number of allowed items, will return the next item to be appended to the list.
We'll set up our backend to send a [`datastar-merge-fragments`](/reference/sse_events#datastar-merge-fragments) event with the `selector` option set to `#list` and the `mergeMode` option set to `append`. This tells Datastar to _append_ the fragments _into_ the `#list` container (rather than the default behaviour of replacing it).
```
event: datastar-merge-fragments
data: selector #list
data: mergeMode append
data: fragments <div>Item 2</div>
```
In addition, we'll send a [`datastar-merge-signals`](/reference/sse_events#datastar-merge-signals) event to update the `offset`.
```
event: datastar-merge-signals
data: signals {offset: 2)
```
In the case when the incoming offset is `2`, we'll remove the button from the DOM entirely.
```
event: datastar-remove-fragments
data: selector #load-more
```
Here's how it might look using the SDKs.
!!!CODE_SNIPPET:how_tos/load_more!!!
## Conclusion
While using the default merge mode of `morph` is generally recommended, appending to a list is a good example of when to use an alternative merge mode.

View File

@ -56,7 +56,7 @@ data: fragments </div>
Be careful not to add `.leading` to the modifier in the response, as it will cause the frontend to immediately send another request.
Here's how it would look using the SDKs.
Here's how it might look using the SDKs.
!!!CODE_SNIPPET:how_tos/polling_1!!!

View File

@ -34,7 +34,7 @@ event: datastar-execute-script
data: script window.location.href = "/guide"
```
Here's how it would look using the SDKs.
Here's how it might look using the SDKs.
!!!CODE_SNIPPET:how_tos/redirect_1!!!