Click To Load

Load More asks the server for the next page and appends it to the list. The handler bumps CurrentPage, fetches that page, and appends the new items; because every row has a stable data-key, the diff engine adds only the new rows and leaves the existing ones untouched. A HasMore flag hides the button at the end of the list.

Click To Load

Append-only pagination. Clicking Load More asks the server for the next page; the diff engine appends new rows without redrawing existing ones.

IDNameEmail
1 Item 1 item1@example.com
2 Item 2 item2@example.com
3 Item 3 item3@example.com
4 Item 4 item4@example.com
5 Item 5 item5@example.com
6 Item 6 item6@example.com
7 Item 7 item7@example.com
8 Item 8 item8@example.com
9 Item 9 item9@example.com
10 Item 10 item10@example.com

Template

The button's name="loadMore" names the action; {{if .HasMore}} swaps it for an "End of list" note once the last page is loaded.

{{define "content"}}
<article>
    <h3>Click To Load</h3>
    <p><small>Append-only pagination. Clicking Load More asks the server for the next page; the diff engine appends new rows without redrawing existing ones.</small></p>
    <table>
        <thead><tr><th>ID</th><th>Name</th><th>Email</th></tr></thead>
        <tbody>
        {{range .Items}}
        <tr data-key="{{.ID}}">
            <td>{{.ID}}</td>
            <td>{{.Name}}</td>
            <td>{{.Email}}</td>
        </tr>
        {{end}}
        </tbody>
    </table>
    {{if .HasMore}}
    <form method="POST">
        <button name="loadMore" class="secondary outline">Load More</button>
    </form>
    {{else}}
    <p><small>End of list.</small></p>
    {{end}}
</article>
{{end}}

click-to-load.tmpl

Handler & state

LoadMore increments the page counter, appends the next slice, and sets HasMore from whether a full page came back.

type ClickToLoadController struct{}

func (c *ClickToLoadController) LoadMore(state ClickToLoadState, ctx *livetemplate.Context) (ClickToLoadState, error) {
	state.CurrentPage++
	newItems := getItemPage(state.CurrentPage, listPageSize)
	state.Items = append(state.Items, newItems...)
	state.HasMore = len(newItems) == listPageSize
	return state, nil
}

func clickToLoadHandler() http.Handler {
	tmpl := newLayoutTmpl("templates/layout.tmpl", "templates/lists/click-to-load.tmpl")
	return tmpl.Handle(&ClickToLoadController{}, livetemplate.AsState(&ClickToLoadState{
		Title:       "Click To Load",
		Category:    "Lists & Data",
		Items:       getItemPage(1, listPageSize),
		CurrentPage: 1,
		HasMore:     true,
	}))
}

handlers_lists.go:90-110

type ClickToLoadState struct {
	Title       string
	Category    string
	Items       []Item
	CurrentPage int
	HasMore     bool
}

state_lists.go:15-22

When to use

Reach for Infinite Scroll when pages should load automatically as the user scrolls.

source: livetemplate/docs · path: examples/patterns/templates/lists/click-to-load.tmpl