Ant design table row merge based on data instead of index value - reactjs

{
title: 'Home phone',
colSpan: 2,
dataIndex: 'tel',
render: (value, row, index) => {
const obj = {
children: value,
props: {},
};
if (index === 2) {
obj.props.rowSpan = 2;
}
// These two are merged into above cell
if (index === 3) {
obj.props.rowSpan = 0;
}
if (index === 4) {
obj.props.colSpan = 0;
}
return obj;
},
},
In dis example based on index value rowsspan size is decided, can we do that based on previous column data (if there are two John Brown's then row span should be 2)?. So basically we need to decide row span size by sorting rows and comparing row value.

https://stackblitz.com/edit/react-bubauz?file=index.js
I can give you an idea with name column, mb it helps you.
If you want to merge Name column:
First of all you need to sort your table data.Then create Set(). And you need clear Set in useEffect().
const names = new Set();
{
title: 'Name',
rowSpan: 1,
dataIndex: 'name',
render: (value, row, index) => {
const obj = {
children: value,
props: {}
};
if (names.has(value)) {
obj.props.rowSpan = 0;
} else {
const occurCount = tableData.filter((data) => data.name === value).length;
obj.props.rowSpan = count;
names.add(value);
}
return obj;
}
}

Related

React setting state using function not merging like objects

This function is called whenever MUI's data grid pro column width has been changed. I am trying to capture the "field" and "width" from the "params" and create an array of objects for all the columns that had changed width. My issue is that it just keeps adding the same object instead of merging them. For instance below I changed the width of the "wave" column two times and it just added the second change to the array.
I need help merging them properly
const handleColumnSizeChange = useCallback(
(params) => {
setColumnDefinitions((columnDefinitions) => {
return [
...columnDefinitions,
{ field: params.colDef.field, width: params.colDef.width },
];
});
},
[columnDefinitions]
);
console.log(columnDefinitions);
UPDATE:
I figured it out. I thought there was an easier way just using the spread in my previous function.
const handleColumnSizeChange = useCallback(
(params) => {
const { field, width } = params.colDef;
const check = columnDefinitions.some((e) => e.field === field);
if (check) {
const updatedDefs = columnDefinitions.map((column) => {
if (column.field === field) {
return { ...column, width: width };
}
return column;
});
setColumnDefinitions(updatedDefs);
} else {
// setColumnDefinitions((columnDefinitions) => {
// return [...columnDefinitions, { field: field, width: width }];
// });
setColumnDefinitions([
...columnDefinitions,
{ field: field, width: width },
]);
}
},
[columnDefinitions]
);
const handleColumnSizeChange = useCallback(
(params) => {
const { field, width } = params.colDef;
const check = columnDefinitions.some((e) => e.field === field);
if (check) {
const updatedDefs = columnDefinitions.map((column) => {
if (column.field === field) {
return { ...column, width: width };
}
return column;
});
setColumnDefinitions(updatedDefs);
} else {
setColumnDefinitions((columnDefinitions) => {
return [...columnDefinitions, { field: field, width: width }];
});
}
},
[columnDefinitions]
);

Generic filter for X number of properties

I want to make a generic filter function. Currently I have a function that looks like this:
const filterRows = () => {
items.filter((item) => {
if(selectedDrinks.length > 0 && selectIds.length > 0) {
return selectedDrinks.includes(item.description) && selectedIds.includes(item.props.id)
}else if(selectedDrinks.length > 0) {
return selectedDrinks.includes(item.description)
}else if(selectedIds.length > 0) {
return selectedIds.includes(item.props.id)
}
}
}
The number of if checks I need to do will grow exponentially if I add one more thing to filter by.
I've made a pathetic try below. One issue I encountered is if I have a nested structure and want to access ["props/id"] as I don't know the syntax for it. Also tried ["props:id"] etc. And if I add multiple strings in the query it does not work either. And even if I could add multiple strings properly it would only work as an OR.
And for me it would be selectedDrinks && selectedId as both need to match for it to filter, not selectedDrinks || selectedIds
I want to include everything in both selectedDrinks and selectedIds as a query, and they should filter only if both are included in "assets" as description and props:id. I should also be able to add e.g "selectedNames" as a third "query parameter".
const selectedDrinks: string[] = [
"cola",
"fanta",
]
const selectedIds : string[] = [
"5",
"4",
]
interface s {
description: string;
name: string;
props: {
id: string
}
}
const items: s[] = [
{
description: "cola",
name: "computer",
props: {
id: "4"
}
},
{
description: "fanta",
name: "laptop",
props: {
id: "5"
}
},
{
description: "sprite",
name: "phone",
props: {
id: "6"
}
}
]
export function genericFilter<T>(
object: T,
filters: Array<keyof T>,
query: string[]
):boolean {
if(query.length === 0)
return true
return filters.some(filter => {
const value = object[filter]
console.log(value)
if(typeof value === "string") {
return value.toLowerCase().includes(query.map(q => q.toLowerCase()).join(""))
}
if(typeof value === "number") {
return value.toString().includes(query.map(q => q.toLowerCase()).join(""))
}
return false
})
}
const myFilterResult = items.filter((asset) => genericFilter(item, ["props", "name"], ["5"]))
console.log(myFilterResult)
If anyone is interested, here is how I solved it.
/**
*
* #returns A new list of filtered objects
* #param objects The objects that we want to filter
* #param properties The properties we want to apply on the object and compare with the query
* #param queries The queries we want to filter by
*/
export function genericFilter<T>(
objects: T[],
properties: Array<keyof T>,
queries: Array<string>[] | Array<number>[]
):T[] {
return objects.filter((object) => {
var count = 0;
properties.some((props) => {
const objectValue = object[props]
if(typeof objectValue === "string" || typeof objectValue === "number") {
queries.forEach((query) => {
query.forEach((queryValue) => {
if(queryValue === objectValue) {
count+=1;
}
})
})
}
})
return count === properties.length;
})
}
export default genericFilter;
How you call the function, can include X amount of filters and strings to search for.
const result = genericFilter(assets, ["description", "id", "name"], [selectedAssetTypes, selectedIds, selectedNames])

Why the table data is not updated immediately after performing an action for the table

I have a table that has an action to delete..like this:
const deleteRow = (row) => {
let indexOfDeleted = -1;
let data = tableData;
data.forEach((item, index) => {
if (item.instrumentId === row.instrumentId) {
indexOfDeleted = index;
}
})
data.splice(indexOfDeleted, 1);
setTableData(data)
};
The data is deleted but I have to refresh it so that it is not displayed in the table.It does not seem to be rerender. What should I do?
for table:
const schema = {
columns: [
{
field: "persianCode",
title: "title",
},
],
operations: [
{
title: "delete",
icon: (
<DeleteIcon
className={clsx(classes.operationsIcon, classes.deleteIcon)}
/>
),
action: (row) => deleteRow(row),
tooltipColor: theme.palette.color.red,
}
],
};
You are mutating the state variable, in your deleteRow function. You should update the state with a copied array:
const deleteRow = (row) => {
setTableData(table => table.filter(data => data.instrumentId !== row.instrumentId))
};
Instead of finding the index and splicing it, you can just use the filter function. Since it returns a new array, we don't worry about mutating the state variable!
you will have to use Spread operator to reflect changes in react dom..
const deleteRow = (row) => {
let indexOfDeleted = -1;
let data = tableData;
data.forEach((item, index) => {
if (item.instrumentId === row.instrumentId) {
indexOfDeleted = index;
}
})
data.splice(indexOfDeleted, 1);
setTableData([...data]) /// like this
};

How to customize React Antd table header with table data?

I want to customer header table like it:
Merge the cells as below:
const columns = [
{
// title: "Title",
colSpan: 1,
// dataIndex: "tel",
render: (value, row, index) => {
const obj = {
children: value,
props: {}
};
if (index === 0) {
obj.props.rowSpan = 0;
}
if (index === 1) {
obj.props.rowSpan = 0; // merge here
}
return obj;
}
}
];
Refer: ant document of components-table-demo-colspan-rowspan

grouping the data based on one property antd table

I am trying to group the data currently its working for three, if i remove any one gender then i wont be able to get it the same design,
Basically if the state_name is same then it should be grouped into same even there is only one gender or more than one as shown below
Codesandbox
Figured out the issue, basically having a variable outside, when the sameKey repeats not setting the count making rowSpan as 0 so it will be hidden.
Logic
let sameKey;
const columns = [
{
title: "District",
dataIndex: "state_name",
key: "state_name",
render: (value, row, index) => {
const obj = {
children: value,
props: {}
};
if (!(sameKey !== value)) {
obj.props.rowSpan = 0;
return obj;
}
const count = data.filter(item => item.state_name === value).length;
sameKey = value;
obj.props.rowSpan = count;
return obj;
}
},
See the codesandbox

Resources