Table pagination using Svelte

Viewed 1589

I'm starting working on the project built in Svelte and never worked with Svelte before. I'm building the table with pagination and the issue I've got at the moment is that I don't know how can I implement pagination buttons working to display different rows. Any suggestions what I should do in the next steps?

<script>
  import { onMount } from "svelte";
  
  import Header from "./components/Header.svelte";
  import Row from "./components/Row.svelte";
  import Footer from "./components/Footer.svelte";
  import Overlay from "./components/Overlay.svelte";

  let rows = [];
  let page = 0;
  let totalPages = [];
  let currentPageRows = [];
  let itemsPerPage = 5;
  let loading = true;

  const paginate = (items) => {
    const pages = Math.ceil(items.length / itemsPerPage);

    const paginatedItems = Array.from({ length: pages }, (_, index) => {
      const start = index * itemsPerPage;
      return items.slice(start, start + itemsPerPage);
    });

    console.log("paginatedItems are", paginatedItems);
    totalPages = [...paginatedItems];
    currentPageRows = paginatedItems[page];
  };

  onMount(() => {
    fetch("devapi/accountStatement/transactions.json")
      .then((response) => {
        if (response.ok) {
          return response.json();
        }
        throw Error(response.status);
      })
      .then((data) => {
        rows = data;
        paginate(data);
        loading = false;
      })
      .catch((error) => {
        console.log(error);
      });
  });

  const nextPageHandler = () => {
    if (page < totalPages.length) {
      page += 1;
    }

    console.log("page is", page);
  };

  const previousPageHandler = () => {
    if (page > 0) {
      page -= 1;
    }

    console.log("page is", page);
  };
</script>

<table class="table table-bordered table-striped table-hover">
  <Header />

  <tbody>
    {#if loading}
      <Overlay />
    {/if}
    {#each currentPageRows as row, i}
      <Row {row} />
    {:else}
      <tr>
        <td colspan="100%">
          <h5 class="text-center">There is no data to display here.</h5>
        </td>
      </tr>
    {/each}
  </tbody>
  <Footer />
</table>
<nav class="pagination">
  <ul>
    <li>
      <button
        type="button"
        class="btn-next-prev"
        on:click={() => previousPageHandler()}
        >PREV
      </button>
    </li>

    {#each totalPages as page, i}
      <li>
        <button type="button" class="btn-page-number">{i + 1}</button>
      </li>
    {/each}

    <li>
      <button
        type="button"
        class="btn-next-prev"
        on:click={() => nextPageHandler()}
        >NEXT
      </button>
    </li>
  </ul>
</nav> 
1 Answers

You were almost there. What you want to do is bring in Svelte's reactive statements, making currentPageRows dependent on the current value of page.

You can do this with a single line of code:

  ...
  let rows = [];
  let page = 0;
  let totalPages = [];
  let currentPageRows = [];
  let itemsPerPage = 5;
  let loading = true;

  $: currentPageRows = totalPages.length > 0 ? totalPages[page] : [];

  const paginate = (items) => {
    ...
  }
  ...

You could also simplify your handling of page navigation with a single function:

  const setPage = (p) => {
    if (p >= 0 && p < totalPages.length) {
      page = p;
    }
  }

And use this function in all your on:click event handlers:

<nav class="pagination">
  <ul>
    <li>
      <button
        type="button"
        class="btn-next-prev"
        on:click={() => setPage(page - 1)}
      >
        PREV
      </button>
    </li>

    {#each totalPages as page, i}
      <li>
        <button
          type="button"
          class="btn-page-number"
          on:click={() => setPage(i)}
        >
          {i + 1}
        </button>
      </li>
    {/each}

    <li>
      <button
        type="button"
        class="btn-next-prev"
        on:click={() => setPage(page + 1)}
      >
        NEXT
      </button>
    </li>
  </ul>
</nav>

See this REPL for a stripped down example.

Related