I using the example provided by React, and I want to have a button on every row and when it is clicked a row to be deleted.
https://codesandbox.io/s/5vy2q8owj4?from-embed
I am new to reactjs, it is possible to do it?
What I thought to do is to add another row with a button and inside the component to have a function like this, I don't know how to call this function from the outside:
{ key: "", name: "", formatter: () => <button onClick={() =>
this.deleteRows(title)}>Delete</button>}
deleteRows = (id) => {
let rows = this.state.rows.slice()
rows = rows.filter(row => row.id !== id)
this.setState({ rows })
}
Thanks
It's possible. You may use getCellActions to achieve this. Here's a working example: https://codesandbox.io/s/5091lpolzk
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.
I have an antd table in which I need to do some operations only when the click event happens on a specific column.
Currently, I am using the onRow prop in the table component as below
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
// some operations
},
};
}}
With this implementation, click is triggered for the entire row ( when clicked on any column )
I have tried to see the column dataIndex or key to return the click event only when the event has happened on a specific column. But args of onRow do not have that data.
Is there a way to achieve the required behavior?
If I understand correctly you want to create a table with editable row. I would suggest to create a new column (e.g. Actions) in which you will have a button that when you click it, it will let you edit the row you want. Check the following link, hope it's what you are looking for:
https://codesandbox.io/s/editable-rows-antd-4-20-6-forked-hvul4u?file=/demo.js
If you are looking to capture click events for a specific column, you can use the onCell property for column. (Codesandbox)
const columns = [
{
title: "Name",
dataIndex: "name",
render: (text, row, index) => <a>{text}</a>,
onCell: (record, rowIndex) => {
return {
onClick: () => {
console.log(record, rowIndex);
}
};
}
},
...
]
I would like to display some json data (coming from my backend and handled in a hook) in a similar way to this : https://ant.design/components/table/#components-table-demo-tree-data
I have a column with a checkbox and a column with an input (both must be editable by the user) and the final json state must be updated with the new datas.
Here you can find the structure of my json : https://pastebin.com/wA0GCs1K
Here you have a screen of the final result :
The code I used to fetch the data :
const [dataServiceInterface, setDataServiceInterface] = useState(null);
useEffect(() => {
CreateApiService.fetchDataServiceInterface(questionFiles, responseFiles).then((result) => {
if (result != null) {
setDataServiceInterface(result);
}
});
}, []);
Here you have the code I used to update the attributes (column constant for the second part) :
const onInputChange = (key, record, index) => (e) => {
e.preventDefault();
console.log(key);
console.log(index);
console.log(record);
console.log(e.target.value);
dataServiceInterface.itemsQ[index][key] = e.target.value;
};
// some other code here
{
title: "Json Name",
dataIndex: "name",
key: "name",
render: (text, record, index) => (
<Input
//defaultValue={text}
value={text}
onChange={onInputChange("name", record, index)}
/>
),
},
Problem is (I think) : As I dont have a key defined in my json datas (parents and children), when I try to update the children it dosent work. I can't change the structure of the json because it's a business constraint. I was thinking of copying my json data in another state, then add the keys ... and still didn't try this solution and don't know if it works. I will update this if it's the case.
Meanwhile, if someone had the same issue and has any idea/hint/suggestion, would appreciate very much. Thx.
I think the problem here is that the component is not rendering the data once you have it. This can be solved by using useState hook, you should lookup for the docs.
I would love to get a little bit more of the component that you are building. But the approach to this would be something like this:
const Component = () {
const [data, useData] = useState([]);
const onInputChange = (key, record, index) => (e) => {
e.preventDefault();
const data = // You get fetch the data here
useData(data);
};
return <>
{
data && data.itemsQ.map((item) => {
// here you display the information contained in each item
})
}
</>
}
I am not able to access onClick parameter inside setState function.
const [tableData, settableData] = useState({ tableDataList: [] });
const deleteRow = (rowId) => {
settableData(tData => {
//I have to access rowId to delete row with rowid from tData here
return tData;
});
}
.
.
//This button is inside a table. Present in all rows
<button className="btn-dark" onClick={() => deleteRow(rowId)}>DELETE</button>
Can any one help me to access rowId inside setState function?
You have to filter out using normal array methods from JavaScript. One example is the filter method that helps you filter the array based on how you like it to be filtered.
The following example will filter the array, returning all items, except for the item that matches the rowId, returning a new array without the deleted item:
tableData.tableDataList.filter(item => item.id !== rowId)
The full example must look something like this:
settableData(tData => {
return tData.tableDataList.filter(item => item.id !== rowId);
});
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',
}]
}