vincjo / datatables

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

[Runes] How to make the table content reactive? #129

Closed icheered closed 1 month ago

icheered commented 2 months ago

I create a simple table component

<script lang="ts">
    import { TableHandler, Datatable, ThSort, ThFilter } from '@vincjo/datatables'

    let {  data }: { data: any[] } = $props()

    const table = new TableHandler(data, { rowsPerPage: 10 })
</script>

<Datatable basic {table}>
    <table>
        <thead>
            <tr>
                <ThSort {table} field="first_name">First Name</ThSort>
                <ThSort {table} field="last_name">Last Name</ThSort>
                <ThSort {table} field="email">Email</ThSort>
            </tr>

            <tr>
                <ThFilter {table} field="first_name" />
                <ThFilter {table} field="last_name" />
                <ThFilter {table} field="email" />
            </tr>
        </thead>
        <tbody>
            {#each table.rows as row}
                <tr>
                    <td>{row.first_name}</td>
                    <td>{row.last_name}</td>
                    <td>{row.email}</td>
                </tr>
            {/each}
        </tbody>
    </table>
</Datatable>

When trying to use this component:

<script lang="ts">
    import Table from '$lib/components/table.svelte'

    let data = $state([
        { id: 1, first_name: 'Tobie', last_name: 'Vint', email: 'tvint0@fotki.com' },
        { id: 2, first_name: 'Zacharias', last_name: 'Cerman', email: 'zcerman1@sciencedirect.com' },
        { id: 3, first_name: 'Gérianna', last_name: 'Bunn', email: 'gbunn2@foxnews.com' },
    ])

    function addNewName() {
        data.push({ id: data.length + 1, first_name: 'test', last_name: 'testlast', email: 'testmail' })
        console.log('Number of data points: ' + data.length)
    }
</script>

<button onclick={addNewName}>Add new name</button>
<Table {data} />

The console logs the correct number of data points, but the table is not updated with the new data.

What is the correct way of 'binding' the data to the table, so that when the data changes the table is also updated?

icheered commented 2 months ago

Small update, I can get the table to update when adding or removing items using the $effect rune but this feels quite dirty:

    let table = $state(new TableHandler($state.snapshot(data)))
    $effect(() => {
        table = new TableHandler($state.snapshot(data), { rowsPerPage: 10 })
    })

However this breaks sorting and filtering using the table headers

vincjo commented 2 months ago

Yes, indeed, data must be updated using table.setRows(new_data):

The raw data is deep cloned, so no longer shares any state with the original object, even if declared as a $state rune.

Instead of data, you need to pass the TableHandler instance to your Datatable component.

Then you can apply table.setRows() in addNewName() which will update the content.

Example:

<script lang="ts">
    import Table from '$lib/components/table.svelte'
    import { TableHandler } from '@vincjo/datatables'

    let data = [
        { id: 1, first_name: 'Tobie', last_name: 'Vint', email: 'tvint0@fotki.com' },
        { id: 2, first_name: 'Zacharias', last_name: 'Cerman', email: 'zcerman1@sciencedirect.com' },
        { id: 3, first_name: 'Gérianna', last_name: 'Bunn', email: 'gbunn2@foxnews.com' },
    ]

    const table = new TableHandler(data, { rowsPerPage: 10 })

    function addNewName() {
        data.push({ id: data.length + 1, first_name: 'test', last_name: 'testlast', email: 'testmail' })
        console.log('Number of data points: ' + data.length)

        table.setRows(data)
    }
</script>

<button onclick={addNewName}>Add new name</button>
<Table {table}/>

In your table component:

<script lang="ts">
    import { type TableHandler, Datatable, ThSort, ThFilter } from '@vincjo/datatables'

    let { table }: { table: TableHandler } = $props()
</script>