javascriptsveltekit

How do I refresh / re-render the page when an array is updated?


I'm trying to modify an existing page to add pagination and filtration to a list that the page is fetching. I managed to get the pagination working, but now I'm trying to make it prettier by not loading all 20,000 page numbers at the bottom of the screen, and devised some code that would show only 20, and ideally the user-selected page would be somewhere in the middle (i.e., if the user selects page 29, the bottom row would show 19 to 39)

Here's my +page.svelte:

<script>
    import { page } from '$app/stores';
    import ProjectBuild from '$lib/components/ProjectBuild.svelte';
    import { getDuration } from '$lib/utils';
    export let data;

    const LIMIT_PER_PAGE = 4;
</script>

<div class="my-12 px-6 pb-48 md:px-24 lg:px-48 xl:px-72">
    <h1 class="text-4xl font-bold text-slate-900 dark:text-white">Builds</h1>
    <div class="grid grid-cols-4 gap-4 px-4 py-4">
        <div class="col-span-1 flex-grow">
            <ul class="mt-6 flex flex-col gap-y-2">
                <!-- Filtration stuff -->
                {#each data.integrationNames as integName}
                <li>
                    <a href="{$page.url.pathname}?limit={LIMIT_PER_PAGE}&pagenum=1&filterby={integName}">
                        {integName}
                    </a>
                </li>
                {/each}
            </ul>
        </div>
        <div class="col-span-3 flex-grow">
            <ul class="mt-6 flex flex-col gap-y-4">
                {#each data.builds as build}
                    <li>
                        <!-- Items per page -->
                        <ProjectBuild />
                    </li>
                {/each}
            </ul>
            {#if !data.builds || data.builds.length === 0}
                <p>No builds found</p>
            {/if}
            <div class="grid grid-rows-2">
                <div class="row-span-1"></div>
                <!-- Pagination -->
                {#if data.pageInfo.totalPages > 1}
                    <div class="row-span-1 flex justify-center gap-x-3">
                        {#each data.pageInfo.pageRange as _, id}
                        <div>
                            <a href="{$page.url.pathname}?limit={LIMIT_PER_PAGE}&pagenum={id + 1}&filterby={data.filter}" 
                            class={data.pageInfo.currentPage == id + 1 ? "font-bold text-slate-900 dark:text-green-400" : "font-bold text-slate-900 dark:text-white"}>
                                {id + 1}
                            </a>
                        </div>
                        {/each}
                    </div>
                {/if}
            </div>
        </div>
    </div>
</div>

And now here's my +page.server.ts:

import type { PageServerLoad } from './$types';
import {
    JSONApiResponse,    
    apiHostname,
    apiCall,
    ProjectBuildWithCountFromJSON,
} from '$lib/api';
import type { ProjectBuild, ProjectBuildsWithCount } from '$lib/types';

export const load: PageServerLoad = async ({ url, params, cookies }) => {
    try {
        const maxPageNumDisplay = 20;
        const limitPerPage = url.searchParams.get('limit') || 4;
        const pageNum = parseInt(url.searchParams.get('pagenum')) || 1;
        const filter = url.searchParams.get("filterby") || "All Builds";
        const [response] = await Promise.all([
            apiCall(`${apiHostname}/v1/project/${params.id}/build?limit=${limitPerPage}&pagenum=${pageNum}`, undefined, cookies).then(
                async (response) => {
                    let transformedJson = // Process results here
                    return transformedJson;
                }
            )
        ]);

        if (response.count !== null &&  response.count !== undefined && response.builds !== null && response.builds !== undefined) {
            let integNames = ["All Builds"];
            let totalPages = Math.ceil(response.count / limitPerPage);
            let filteredBuilds: ProjectBuild[] = [];
            response.builds.forEach((build) => {
                if (!integNames.includes(build.integrationName)) {
                    integNames.push(build.integrationName);
                }
            });

            if (filter == "All Builds") {
                filteredBuilds = response.builds;
            } else {
                response.builds.forEach((build) => {
                    if (build.integrationName === filter) {
                        filteredBuilds.push(build);
                    }
                })
            }

            // This is where I tried to prettify my pagination by limiting the page numbers displayed to 20
            let pageRange: Number[] = [];
            let start = 1;
            let end = totalPages;
            if (totalPages > maxPageNumDisplay) {
                end = maxPageNumDisplay
                if (pageNum >= maxPageNumDisplay) {
                    start = pageNum - (maxPageNumDisplay / 2);
                    if (pageNum + (maxPageNumDisplay / 2) < totalPages) {
                        end = pageNum + (maxPageNumDisplay / 2);
                    } else {
                        end = totalPages;
                    }
                }               
            }
            pageRange = Array.from({ length: (end - start) / 1 + 1 }, (value, index) => start + index * 1);

            return {
                pageInfo: {
                    currentPage: pageNum,
                    totalPages: totalPages,
                    pageRange: pageRange
                },
                filter: filter,
                builds: filteredBuilds,
                integrationNames: integNames
            };
        }

        return {
            pageInfo: {
                currentPage: pageNum,
                totalPages: 1,
                pageRange: [1]
            },
            filter: "All Builds",
            builds: [],
            integrationNames: []
        };
    } catch (err) {
        throw err;
    }
};

My problem right now is that the page doesn't seem to reload; from the get go, it displays page numbers 1 to 20. If I select 20, ideally it would show page numbers 10 to 30. But instead it just shows 1 to 21.

Logging out the pageRange variable via console.log shows that it does change to the appropriate page number range, so I'm thinking it's just the Svelte page not reloading or re-rendering or refreshing itself.

I tried adding the following to +page.svelte:

$: pageRange = data.pageInfo.pageRange
...
{#each pageRange as _, id}
...

But it doesn't change the outcome, and curiously, outputting the pageRange variable tells me it's undefined.

Am I going about this the wrong way, or did I miss something in my code? Pagination and filtration does update the list of items shown, if that means anything.


Solution

  • As it turns out, changing the following in +page.svelte:

    {#each data.pageInfo.pageRange as _, id}
        <div>
            <a href="{$page.url.pathname}?limit={LIMIT_PER_PAGE}&pagenum={id + 1}&filterby={data.filter}" class={data.pageInfo.currentPage == id + 1 ? "font-bold text-slate-900 dark:text-green-400" : "font-bold text-slate-900 dark:text-white"}>
                {id + 1}
            </a>
        </div>
    {/each}
    

    Into this:

    {#each pageRange as pagenum}
        <div>
            <a href="{$page.url.pathname}?limit={LIMIT_PER_PAGE}&pagenum={pagenum}&filterby={data.filter}" class={data.buildPageInfo.currentPage == pagenum ? "font-bold text-slate-900 dark:text-green-400" : "font-bold text-slate-900 dark:text-white"}>
                {pagenum}
            </a>
        </div>
    {/each}
    

    Did the trick.