Skip to content

Paginating Children

Stuart George edited this page Jan 17, 2025 · 9 revisions

Currently the children are just listed out to the page. You may want to use pagination.

Create Generic Paginator

Create a generic paginator struct to show a subset of the pages children.

We will keep it generic so that we can use it in other cases. If you dont want this, use []Relation as the type for Items:

type Paginator[T any] struct {
    Items    []T
    PageNum  int
    PageSize int
}

func (p *Paginator[T]) Results() []T {
	start := int((p.PageNum - 1) * p.PageSize)
	if start >= len(p.Items) || start < 0 {
        return []T{}
    }
    end := start + p.PageSize
    if end > len(p.Items) {
        end = len(p.Items)
    }
    return p.Items[start:end]
}

func (p *Paginator[T]) TotalPages() int {
    return int(math.Ceil(float64(len(p.Items)) / float64(p.PageSize)))
}

func (p *Paginator[T]) HasNext() bool {
    return p.PageNum < p.TotalPages()
}

func (p *Paginator[T]) HasPrev() bool {
    return p.PageNum > 1
}

Update UI

Update the ui.pages.PageTypeListing struct and its body func to use the new paginator:

type PageTypeListing struct {
	PageNum  int
	PageSize int
}
templ (t *PageTypeListing) Body(page *Page) {
	{{ paginator := Paginator[Relation]{page.Children, t.PageNum, t.PageSize} }}
	<body class="bg-white text-black flex min-h-screen flex-col antialiased space-y-10">
		@page.Header()
		<main class="container mx-auto px-6 flex-grow">
			<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
				for _, child := range paginator.Results() {
					<a class="space-y-3" href={ templ.URL(child.Url) }>
						<img class="w-full" src={ child.FeaturedImage } alt={ child.Title }/>
						<div class="text-xl font-semibold tracking-tight">{ child.Title }</div>
						<div>{ child.Meta.Description }</div>
					</a>
				}
			</div>
		</main>
		if paginator.TotalPages() > 1 {
			<section class="container mx-auto px-6 flex">
				if paginator.HasPrev() {
					<a class="mr-auto" href={ templ.URL(fmt.Sprintf("?page=%d", t.PageNum-1)) }>Prev</a>
				}
				if paginator.HasNext() {
					<a class="ml-auto" href={ templ.URL(fmt.Sprintf("?page=%d", t.PageNum+1)) }>Next</a>
				}
			</section>
		}
		@page.Footer()
	</body>
}

Update Handler

Finally update the handler func to pass the query params to use the paginator:

func handleGenericPageListing(c echo.Context, queries *dbx.Queries, page *dbx.Page) (pages.PageType, error) {
	pageStr := c.QueryParam("page")
	pageNum, err := strconv.Atoi(pageStr)
	if err != nil || pageNum < 1 {
		pageNum = 1
	}

	return &pages.PageTypeListing{
		PageNum:  pageNum,
		PageSize: 3,
	}, nil
}

Clone this wiki locally