I am trying to create a new checkbox column for a react table. I don't need a header name
for the checkbox column. If I put in an empty string in the header, my app crashes and I get the error message below
const columns = React.useMemo(
() => [
{
//id: 'checkbox',
Header: '',
accessor: '',
width: 25,
Cell: ({ row }: any) => {
return <input type='checkbox' className={classes.mystyle} />
},
},
{
Header: 'Jungle Name',
accessor: 'JungleMan',
}
]
)
How can I do this with an empty Header?
The answers provided in the comments are technically correct, but also very much a hack. There is a non-workaround method of doing this: simply provide an id key to the Column. This use of id is described in the useTable hook docs:
Technically speaking, [accessor] isn't required if you have a unique id for a column.
A name like "checkbox" won't fly, because of the requirement to be unique. Thus, it should probably be slightly more descriptive. Furthermore, Header is explicitly an Optional property -- you can omit it entirely, to no detriment.
Therefore, your column can just look like:
const columns = React.useMemo(
() => [
{
id: 'checkbox-table-column'
width: 25,
Cell: ({ row }: any) => {
return <input type='checkbox' className={classes.mystyle} />
},
},
{
Header: 'Jungle Name',
accessor: 'JungleMan',
}
],
[]
);
(Also: fixed the minor typo where the dependency array wasn't passed to useMemo. It'll let you do it, but less chances for failure if you pass one, even empty, yourself.)
Add a custom key hideHeader: false, in the columns variable.
hideHeader: false, in the columns variable
In the Material table check for this key.
<TableHead>
{headerGroups.map((headerGroup) => (
<TableRow {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => {
return column.hideHeader === false ? null : (
<TableCell {...column.getHeaderProps()}>
{column.render('Header')}
</TableCell>
);
})}
</TableRow>
))}
</TableHead>
paste to
This should remove the empty row from html output.
credit to link
Related
I am currently trying to implement React-Table, with a data structure which matches this typescript definition.
export type VendorContent = {
id: number;
name: string;
user_name: string;
dob: string;
};
export type VendorData = {
vendor: string;
rows: VendorContent[];
};
<DataTable defaultData={vendorData} />
Structurally, the design I have looks like this:
Within the DataTable itself, I have something like this:
const columns = [
columnHelper.display({
id: 'actions',
cell: (props) => <p>test</p>,
}),
columnHelper.accessor('name', {
header: () => 'Name',
cell: (info) => info.renderValue(),
}),
columnHelper.accessor('user_name', {
header: () => 'User name',
cell: (info) => info.renderValue(),
}),
columnHelper.accessor('dob', {
header: () => 'DOB',
cell: (info) => info.renderValue(),
}),
];
const DataTable = (props: DataTableProps) => {
const { defaultData } = props;
const [data, setData] = React.useState(() =>
defaultData.flatMap((item) => item.rows)
);
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
Now, here's the kicker. Vendor1, and Vendor2 are collapsible rows, and need to be somehow passed into the table, but the defaultData.flatMap((item) => item.rows) which sets up each row, is obviously removing this information / structure. Ergo, I've nothing to hook into to try and render that in the table.
Things I've tried:
const [data, setData] = React.useState(() =>
defaultData
);
Once I try and pass the full Data object in, the column definition complains. (Data passed is no longer an array).
getSubRows within the React Table hook seems to require a full definition of all the columns (all I want is the vendor name there).
Header groups seem to be rendered before the headings, but what I actually want is almost a 'row group' that is expandable / collapsible?
How would I achieve a design similar to the below, with a data structure as illustrated, such that there are row 'headings' which designate the vendor?
I've setup a codesandbox here that sort of illustrates the problem: https://codesandbox.io/s/sad-morning-g5is0e?file=/src/App.js
First steps
Starting from this docs and this example from docs we can create a colapsable row like this (click on the vendor to expand/collapse next rows).
Steps to do it:
Import useExpanded and add it as the second argument of useTable (after the object containing { columns, data })
Replace defaultData.flatMap((item) => item.rows) with myData.map((row) => ({ ...row, subRows: row.rows })) (or if you can just rename rows to subRows and you can just send defaultData (without any mapping or altering of the data).
Add at the beginning of const columns = React.useMemo(() => [ the following snippet:
{
id: "vendor",
Header: ({ getToggleAllRowsExpandedProps }) => (
<span {...getToggleAllRowsExpandedProps()}>VENDOR</span> // this can be changed
),
Cell: ({ row }) =>
row.original.vendor ? <span {...row.getToggleRowExpandedProps({})}>row.vendor</span> : null, // render null if the row is not expandable
},
4. Add the DOB to the columns
Formatting the rows
With some reverse engineering from this question (using colspan) we can render only one value per row (reverse because we want the main row to use all 4 cells).
This will also make the first header part very small and lead to something like this for example.
How we got here from First steps:
We rendered the first cell if the original row has any value for vendor key and
We expanded the cell (in this case) for a span of 4 rows
Main difference in a snippet:
{
row.original.vendor ? (
<td {...row.cells[0].getCellProps()} colSpan={4}>
{row.cells[0].render("Cell")}
</td>
) : (
row.cells.map((cell) => {
return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>;
})
);
}
Unfortunately I don't think there is another (easier / more straight forward) way to do it (I mean I don't think this is bad, but I think it can be confusing especially if you try to figure it out searching trough so many pages of docs and there is no guide in this direction as far as I know).
Also please note I tried to highlight and explain the process. There might be some small extra adjustments needed in the code.
Hi guy I just do experiment on build bulk delete on tanstack React Table
the problem is here I can't change the id of the selected column
why do I want to change this?
because I want MongoDB id that can send to the server.
with selectedRowIds method
here some code from hook
hooks => {
hooks.visibleColumns.push(columns => [
// Let's make a column for selection
{
id: 'selection',
// accessor: '_id',
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => {
console.log(row)
return <div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
},
},
...columns,
])
and all full reference comes from the main documentation
use selectedFlatRows is contian all selected row data
There is an option you can pass to the useReactTable hook called getRowId that allows you to customize your row id.
getRowId?: (
originalRow: TData,
index: number,
parent?: Row<TData>
) => string
https://tanstack.com/table/v8/docs/api/core/table#getrowid
Header: "Delete",
id:'delete',
accessor: str => "delete",
Cell: (row)=> (
<span onClick={(row) => this.onRemoveHandler(row,props)} style={{cursor:'pointer',color:'blue',textDecoration:'underline'}}>
Delete
</span>
)
React Table
This is related to the header delete span link.The code snippets shows the render the delete label with hyperlink.
Here once a user click on delete link how can I get the id of that particular row.
ID has been already assgined to all the row from the json data.
So,How to pass the cellInfo or rowInfo inside the onClick function .
If you check out the docs (specificaly under 'Renderers'), the row object the cell receives is in the following format:
{
// Row-level props
row: Object, // the materialized row of data
original: , // the original row of data
index: '', // the index of the row in the original array
viewIndex: '', // the index of the row relative to the current view
level: '', // the nesting level of this row
nestingPath: '', // the nesting path of this row
aggregated: '', // true if this row's values were aggregated
groupedByPivot: '', // true if this row was produced by a pivot
subRows: '', // any sub rows defined by the `subRowKey` prop
// Cells-level props
isExpanded: '', // true if this row is expanded
value: '', // the materialized value of this cell
resized: '', // the resize information for this cell's column
show: '', // true if the column is visible
width: '', // the resolved width of this cell
maxWidth: '', // the resolved maxWidth of this cell
tdProps: '', // the resolved tdProps from `getTdProps` for this cell
columnProps: '', // the resolved column props from 'getProps' for this cell's column
classes: '', // the resolved array of classes for this cell
styles: '' // the resolved styles for this cell
}
Depending on what your input data looks like, you can use this information to delete from the dataset. If you plan on dynamically editing your data, you should store it in the state, so that the table component can update according to your edits. Assuming that in your state, you save your dataset as data, and use that to populate the table, you can alter the state in your onclick function:
Header: "Delete",
id:'delete',
accessor: str => "delete",
Cell: (row)=> (
<span onClick={() => {
let data = this.state.data;
console.log(this.state.data[row.index]);
data.splice(row.index, 1)
this.setState({data})
}}>
Delete
</span>
)
so a rough approximation of your app would like this:
this.state = {
data: <your data set>
}
<ReactTable
data={this.state.data}
columns={[
<other columns you have>,
{
Header: "Delete",
id:'delete',
accessor: str => "delete",
Cell: (row)=> (
<span style={{cursor:'pointer',color:'blue',textDecoration:'underline'}}
onClick={() => {
let data = this.state.data;
console.log(this.state.data[row.index]);
data.splice(row.index, 1)
this.setState({data})
}}>
Delete
</span>
)}
]}
/>
And of course, you don't need to log that row to the console, that doesn't need to be there. This is also just the quickest and easiest way to handle it, you could instead use the row object to get any specific element you want (id, name, etc.) and use that to remove from the dataset
An important note though: There is a big difference between viewIndex and index, index is what you want to use for your specific case
If you are like me and are using React-Table v7 and you are also using a hooks based approach in your components you will want to do it this way.
const [data, setData] = useState([]);
const columns = React.useMemo(
() => [
{
Header: 'Header1',
accessor: 'Header1Accessor',
},
{
Header: 'Header2',
accessor: 'Header2Accessor',
},
{
Header: 'Delete',
id: 'delete',
accessor: (str) => 'delete',
Cell: (tableProps) => (
<span style={{cursor:'pointer',color:'blue',textDecoration:'underline'}}
onClick={() => {
// ES6 Syntax use the rvalue if your data is an array.
const dataCopy = [...data];
// It should not matter what you name tableProps. It made the most sense to me.
dataCopy.splice(tableProps.row.index, 1);
setData(dataCopy);
}}>
Delete
</span>
),
},
],
[data],
);
// Name of your table component
<ReactTable
data={data}
columns={columns}
/>
The important part is when you are defining your columns make sure that the data in your parent component state is part of the dependency array in React.useMemo.
I am trying to pass a function to be used within ReactTable. Note that I'm referring specifically to https://react-table.js.org/
This is what I've attempted.
I defined MyFunctionCallback() in the parent of MyWrapper, and pass it through to ReactTable, for example:
<ReactTable
MyFunctionCallback = this.props.MyFunctionCallback
data = etc
columns = etc...
/>
and use it like this...
class MyWrapper extends Component{
render() {
var rowsArray = this.props.MyDataArray
var colsDef = [
{
Header: "First name",
accessor: "fname"
},
{
Header: "Last name",
accessor: "lname"
},
{
Header: "Select",
Cell: ({row}) => (<a href='#' onClick={(row) => this.props.MyFunctionCallback('abc', row._original.ID)}> View
</a>
)
}
]
return (
<ReactTable
MyFunctionCallback = this.props.MyFunctionCallback
data ={rowsArray}
columns ={colsDef}
...etc...
/>
); //end-return
} //end-render
}
export default MyWrapper;
Almost works.. except this is not the correct way of doing it, otherwise i would not be asking the question.
How to do it? (please keep it simple, don't need any fancy HOC :-)
Update
onClick={(row) => etc... should read onClick={() => etc...
use accessor: 'ID' so that you don't have to use row._original.ID, simply do row.ID
so corrected section should be:
{
Header: "Select",
accessor: "ID",
Cell: ({row}) => (
<a href='#' onClick={() => this.props.MyFunctionCallback('abc', row.ID)}>
View
</a>
)
}
Everything looks fine in your code except
Change
<ReactTable
MyFunctionCallback = this.props.MyFunctionCallback //this is not required
data ={rowsArray}
columns ={colsDef}
...etc...
/>
To
<ReactTable
data ={rowsArray}
columns ={colsDef}
...etc...
/>
I have an antd table where the data inside one of the columns can get pretty large. I am showing this data in full when the row is expanded but because the cell with a lot of data is on the right side of the screen and the expander icon is on the left side of the screen it is not very intuitive. What I would like to do is move the expander icon inside the actual cell so that the user knows they can click the + to see the rest of the data.
Thanks in advance.
Yes, you can and you have to dig a little deeper their docucmentation...
According to rc-table docs you can use expandIconColumnIndex for the index column you want to add the +, also you have to add expandIconAsCell={false} to make it render as part of the cell.
See Demo
This is how you can make any column expendable.
First add expandedRowKeys in your component state
state = {
expandedRowKeys: [],
};
Then you need to add these two functions onExpand and updateExpandedRowKeys
<Table
id="table-container"
rowKey={record => record.rowKey}
className={styles['quote-summary-table']}
pagination={false}
onExpand={this.onExpand}
expandedRowKeys={this.state.expandedRowKeys}
columns={columns({
updateExpandedRowKeys: this.updateExpandedRowKeys,
})
}
dataSource={this.data}
oldTable={false}
/>
This is how you need to define the function so
that in expandedRowKeys we will always have
updates values of expanded rowKeys
onExpand = (expanded, record) => {
this.updateExpandedRowKeys({ record });
};
updateExpandedRowKeys = ({ record }) => {
const rowKey = record.rowKey;
const isExpanded = this.state.expandedRowKeys.find(key => key === rowKey);
let expandedRowKeys = [];
if (isExpanded) {
expandedRowKeys = expandedRowKeys.reduce((acc, key) => {
if (key !== rowKey) acc.push(key);
return acc;
}, []);
} else {
expandedRowKeys.push(rowKey);
}
this.setState({
expandedRowKeys,
});
}
And finally, you need to call the function updateExpandedRowKeys
for whichever column you want to have the expand-collapse functionality available.
Even it can be implemented for multiple columns.
export const columns = ({
updateExpandedRowKeys,
}) => {
let columnArr = [
{
title: 'Product',
key: 'productDes',
dataIndex: 'productDes',
className: 'productDes',
render: (text, record) => (
<span onClick={rowKey => updateExpandedRowKeys({ record })}>
{text}
</span>
),
}, {
title: 'Product Cat',
key: 'productCat',
dataIndex: 'productCat',
className: 'product-Cat',
}]
}