Implementing checkbox using TanStack table in Solidjs

180 views Asked by At

I'm struggling to get the checkbox working using TanStack in Solidjs. The docs doesn't have much information about creating a checkbox column which is why i followed the TanStack React Table Examples and some of the Solidjs Table Examples. The checkbox would not update and i am assuming this has something to do with Solidjs reactivity. Here props is not updating since i tried to console.log it and it only ran once which was when the component rendered.


const checkbox = {
accessorKey: "checkbox",
        header: (props) => (
            <Checkbox
                {...{
                    checked: props.table.getIsAllRowsSelected(),
                    indeterminate: props.table.getIsSomeRowsSelected(),
                    onChange: props.table.getToggleAllRowsSelectedHandler(),
                }}
            />
        ),
        cell: (props) => {


            return (
                <Checkbox
                    {...{
                        checked: props.row.getIsSelected(),
                        disabled: !props.row.getCanSelect(),
                        onChange: props.row.getToggleSelectedHandler(),
                    }}
                />
            );
        },
    };

If i do it without TanStack, it would look something like this but it defeats the purpose of using TanStack

cell: () => {
            const [isSelected , setIsSelected] = createSignal(false)

            const toggleSelected = (event , checked) => {
                setIsSelected(checked)
            }

            return (
                <Checkbox
                    {...{
                        checked: isSelected(),
                        onChange: toggleSelected,
                    }}
                />
            );
        },

*** Update 1: Here is the whole code of creating the table


const generateColumns = (columns) => {
    const result = columns.map((value) => {
        return {
            accessorKey: value,
        };
    });

    return result;
};

const DataTable = (props) => {
    const [rowSelection, setRowSelection] = createSignal({});

    const checkbox = {
        accessorKey: "checkbox",
        header: (props) => (
            <Checkbox
                {...{
                    checked: props.table.getIsAllRowsSelected,
                    indeterminate: props.table.getIsSomeRowsSelected,
                    onChange: props.table.getToggleAllRowsSelectedHandler,
                }}
            />
        ),
        cell: () => {
            const [isSelected , setIsSelected] = createSignal(false)

            const toggleSelected = (event , checked) => {
                setIsSelected(checked)
            }

            return (
                <Checkbox
                    {...{
                        checked: isSelected(),
                        onChange: toggleSelected,
                    }}
                />
            );
        },
    };

    const columns = [checkbox, ...generateColumns(props.columns)];

    const table = createSolidTable({
        get data() {
            return props.data;
        },
        columns: columns,
        state: {
            get rowSelection() {
                return rowSelection();
            },
        },
        onRowSelectionChange: setRowSelection,
        getCoreRowModel: getCoreRowModel(),
    });

    return (
        <TableContainer component={Paper}>
            <Table>
                <TableHead>
                    <For each={table.getHeaderGroups()}>
                        {(headerGroup) => (
                            <TableRow>
                                <For each={headerGroup.headers}>
                                    {(header) => (
                                        <TableCell>
                                            {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
                                        </TableCell>
                                    )}
                                </For>
                            </TableRow>
                        )}
                    </For>
                </TableHead>
                <TableBody>
                    <For each={table.getRowModel().rows}>
                        {(row) => (
                            <TableRow>
                                <For each={row.getVisibleCells()}>
                                    {(cell) => <TableCell>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCell>}
                                </For>
                            </TableRow>
                        )}
                    </For>
                </TableBody>
            </Table>
        </TableContainer>
    );
};

1

There are 1 answers

2
axelra82 On

I was in a similar spot, coming from the React version of Tanstack Table.

Having looked at your code I was able to make mine work with some minor modifications based on your implementation.

Hopefully this can help you finalize a working solution as well.

Here's the code for my SolidJS table component with working select column:

export const Table: Component<TableData> & TableSubComponentsInterface = (props) => {
    const merged = mergeProps({
        enableRowSelection: () => true,
    }, props);

    const isWorking = useWorking();

    const [sorting, sortingSet] = createSignal<ColumnSort[]>();
    const [rowSelection, rowSelectionSet] = createSignal<RowSelectionState>();
    const [columnFilters, setColumnFilters] = createSignal<ColumnFilter[]>();

    const tableOptions = {
        get data() {
            return merged.columnData ?? [];
        },
        // eslint-disable-next-line solid/reactivity
        columns: merged.columns ?? [],
        state: {
            get rowSelection() {
                return rowSelection();
            },
            get sorting() {
                return sorting();
            },
            get columnFilters() {
                return columnFilters();
            },
        },
        enableRowSelection: (row: TanstackRow<Record<string, unknown>>) => merged.enableRowSelection(row),
        onColumnFiltersChange: setColumnFilters,
        getFilteredRowModel: getFilteredRowModel(),
        getCoreRowModel: getCoreRowModel(),
        getSortedRowModel: getSortedRowModel(),
        onSortingChange: sortingSet,
        onRowSelectionChange: rowSelectionSet,
    };

    const solidTable = createSolidTable(tableOptions);

    return (
            <section class="no-scrollbar table-rounded-md max-w-xl7 w-full overflow-auto rounded-md border border-gray-200 bg-white drop-shadow-sm">
                <table class="w-full">
                    <thead>
                        <For each={solidTable.getHeaderGroups()}>{(headerGroup) => (
                            <tr

                                class="items-center border-b border-b-gray-200 text-left font-semibold capitalize"
                            >
                                <For each={headerGroup.headers}>{(header) => (
                                    <th colSpan={header.colSpan}>
                                        <Show when={!header.isPlaceholder}>
                                            <div
                                                class={`${header.column.getCanSort() ? "cursor-pointer select-none" : ""} flex items-center gap-x-2 p-4`}
                                                onClick={header.column.getToggleSortingHandler()}
                                            >
                                                {flexRender(header.column.columnDef.header, header.getContext())}
                                                {{
                                                    asc: <ExpandLessIcon size={Size.XS} />,
                                                    desc: <ExpandMoreIcon size={Size.XS} />,
                                                }[header.column.getIsSorted() as string]
                                                    ?? (header.column.getCanSort() ? (
                                                        <UnfoldMoreIcon size={Size.SM} />
                                                    ) : null)}
                                            </div>
                                        </Show>
                                    </th>
                                )
                                }</For>
                            </tr>
                        )}</For>
                    </thead>
                    <tbody>
                        <For each={solidTable.getRowModel().rows}>{(row) => (
                            <tr class=" border-b border-b-gray-200 hover:bg-gray-50">
                                <For each={row.getVisibleCells()}>{(cell) => (
                                    <td class="p-4">
                                        {flexRender(cell.column.columnDef.cell, cell.getContext())}
                                    </td>
                                )}</For>
                            </tr>
                        )}</For>
                    </tbody>
                </table>
            </section>
    );
};

Excerpt usage:

import { createColumnHelper } from "@tanstack/solid-table";

const TableUsageComponent = () => {
const columnHelper = createColumnHelper<Record<string, unknown>>();
const columns = [
    columnHelper.accessor("id", selectColumn), // This would be the select column
    // Other required columns for your use case.
]

// In case you have fetching/loading logic.
const isFetching = false;

// Data for columns should go here.
const columnData = [
  {
    key1: "some value",
    key2: "some value 2",
    // ...
  }
];

return (
    <Show when={!isFetching} fallback={<TableLoader />}>
        <Table
            columnData={columnData}
            columns={columns}
        />
    </Show>
);
}

export default TableUsageComponent;

The selectColumn column helper variable (where Checkbox component is a custom component in this case. You might have to accommodate any properties or parameters for you checkbox component):

import { Row as TanstackRow, Table as TanstackTable } from "@tanstack/solid-table";
import { Show } from "solid-js";

import { Checkbox } from "../form";

export const selectColumn = {
    id: "select",
    header: (props: { table: TanstackTable<Record<string, unknown>>; }) => (
        <Checkbox
            {...{
                checked: props.table.getIsAllRowsSelected(),
                indeterminate: !props.table.getIsAllRowsSelected() && props.table.getIsSomeRowsSelected(),
                onChange: props.table.getToggleAllRowsSelectedHandler(),
            }}
        />
    ),
    cell: (props: { row: TanstackRow<Record<string, unknown>>; }) => (
        <Show when={props.row.getCanSelect()}>
            <div class="px-1">
                <Checkbox
                    {...{
                        checked: props.row.getIsSelected(),
                        indeterminate: props.row.getIsSomeSelected(),
                        onChange: props.row.getToggleSelectedHandler(),
                    }}
                />
            </div>
        </Show>
    ),
    enableSorting: false,
};