vincjo / datatables

A toolkit for creating datatable components with Svelte
https://vincjo.fr/datatables
MIT License
363 stars 15 forks source link

Need to manually set the rows in the reload function #102

Closed Shavinder closed 3 weeks ago

Shavinder commented 3 weeks ago

Hi, I am using this code in my +page.svelte:

<script>
    import { DataHandler } from '@vincjo/datatables/remote';
    import RowCount from '$lib/components/datatable/RowCount.svelte';
    import Pagination from '$lib/components/datatable/Pagination.svelte';
    import RowsPerPage from '$lib/components/datatable/RowsPerPage.svelte';
    //import Search from '$lib/components/datatable/Search.svelte';

    export let data;
    let services = data.items;
    let pageNumber = data.skip/data.limit;
    let rowsPerPage = data.limit;
    let totalRows = data.count;

    const reload = async (state) => {
        const response = await fetch(`/api/services${getParams(state)}`);
        const json = await response.json();
        state.setTotalRows(json.count);
        console.log(json.items);
        handler.setRows(json.items); // I am forced to do this because the following line did not have any effect
        //return json.items; 
    }

    const getParams = ({ offset, rowsPerPage, search }) => {
        const page = `limit=${rowsPerPage}&skip=${offset}`

        if (search) {
            return `/search?q=${search}&${page}`
        }
        return `?${page}`
    }

    const handler = new DataHandler(services, { pageNumber: pageNumber, rowsPerPage: rowsPerPage, totalRows: totalRows });
    const rows = handler.getRows();

    handler.onChange((state) => reload(state));

</script>

<div>
    <a href="/app/settings">
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
            <path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8"/>
        </svg>
    </a>
</div>

<div class="flex justify-between items-center mb-4">
    <h2 class="h2 mb-4">Services</h2>
    <a href="/app/settings/services/create" class="btn variant-filled">Add</a>
</div>

<div class="table-container space-y-4">
    <header class="flex justify-between gap-4">
        <div />
        <RowsPerPage {handler} />
    </header>

    <table class="table table-hover table-comfortable">
      <thead>
        <tr>
          <th>Name</th>
          <th>Description</th>
          <th>Cost</th>
        </tr>
      </thead>
      <tbody>
        {#each $rows as row}
          <tr>
            <td>{row.name}</td>
            <td>{row.description}</td>
            <td>{row.cost}</td>
          </tr>
        {/each}
      </tbody>
    </table>
    <footer class="flex justify-between">
        <RowCount {handler} />
        <Pagination {handler} />
    </footer>

  </div>

You'll see that I am manually setting the rows on the handler in the reload function. If I don't, even though the state changes via the UI and the data is fetched correctly, the table rows do not change. Any idea what am I doing wrong?

vincjo commented 3 weeks ago

Hi,

Do you trigger reload in your Pagination and Search components using handler.invalidate() ?

Example for pagination:

 const setPage = (value: 'previous' | 'next' | number) => {
      handler.setPage(value)
      handler.invalidate()
 }
Shavinder commented 3 weeks ago

Hello, Thanks for the quick reply! Much appreciated. Yes I have this in my Paginator component:

<script>
    export let handler;

    const pageNumber = handler.getPageNumber();
    const pageCount = handler.getPageCount();
    const pages = handler.getPages({ ellipsis: true });

    const setPage = (value) => {
        handler.setPage(value);
        handler.invalidate();
    };
</script>

<!-- Desktop buttons -->
<section class="btn-group variant-ghost-surface [&>*+*]:border-surface-500 h-10 hidden lg:block">
    <button
        type="button"
        class="hover:variant-soft-secondary"
        class:disabled={$pageNumber === 1}
        on:click={() => setPage('previous')}
    >
        ←
    </button>
    {#each $pages as page}
        <button
            type="button"
            class="hover:variant-soft-secondary"
            class:active={$pageNumber === page}
            class:ellipse={page === null}
            on:click={() => setPage(page)}
        >
            {page ?? '...'}
        </button>
    {/each}
    <button
        type="button"
        class="hover:variant-soft-secondary"
        class:disabled={$pageNumber === $pageCount}
        on:click={() => setPage('next')}
    >
        →
    </button>
</section>

<!-- Mobile buttons -->
<section class="lg:hidden">
    <button
        type="button"
        class="btn variant-ghost-surface mr-2 mb-2 hover:variant-soft-secondary"
        class:disabled={$pageNumber === 1}
        on:click={() => setPage('previous')}
    >
        ←
    </button>
    <button
        type="button"
        class="btn variant-ghost-surface mb-2 hover:variant-soft-secondary"
        class:disabled={$pageNumber === $pageCount}
        on:click={() => setPage('next')}
    >
        →
    </button>
</section>
vincjo commented 3 weeks ago

Ok fine

For what I saw in the code, your load function just need to return rows as you probably try initially and commented:

const reload = async (state) => {
     const response = await fetch(`/api/services${getParams(state)}`);
     const json = await response.json();
     state.setTotalRows(json.count);
     console.log(json.items);
-    handler.setRows(json.items); 
+    return json.items; 
}

invalidate() executes this load function and updates rows according to what it returns (your server data)

Otherwise I really don't see what is missing

Shavinder commented 3 weeks ago

I don't know what happened. Maybe it's because I restarted the dev server, but it's working now! I swear I have been at this for at least 3 hours and now return json.items; is having the desired effect. Thanks! Your prompt help is much appreciated.