ant design table with dynamic children columns - reactjs

I'm using ant's table and trying to setup dynamic columns.
What I need is a table that shows a list of users with their performance for each of the classes as in the example below.
Details: I have a grouped column Performance that can have different sub-columns (current example shows columns science and physics). I'm calling renderContent() which sets up an object that has property children. I found this "solution" from ant's example here. The problem is that ant's example outputs children prop type string, while my function outputs prop type array. Which results in the error.
Here is a link to sandbox:
https://codesandbox.io/embed/ecstatic-cookies-4nvci?fontsize=14
Note: if you uncomment children array in columns [line 46-59], you will see what my expected result should be.

The render method shouldn't return the object with children array. To use the render method, you would have to return a valid React component (or simply HTML tag ---like span).
However in your case, I prefer we extract subjects before passing it into the table and then generate children array dynamically. Something like below:
const renderContent = (value, row, index) => {
return setupPerformance(value)
};
const setupPerformance = performance => {
return performance.map(p => {
const { id, title, percentage } = p;
return <span>{percentage}%</span>
});
};
const data = [
{
key: 0,
firstName: "John",
lastName: "Smith",
performance: [
{
id: 1,
title: "science",
percentage: 75
},
{
id: 2,
title: "physics",
percentage: 36
}
]
},
{
key: 1,
firstName: "Ann",
lastName: "Smith",
performance: [
{
id: 1,
title: "science",
percentage: 68,
timeSpent: 50,
completionDate: "2019-02-07"
},
{
id: 2,
title: "physics",
percentage: 100
}
]
}
];
let subjects = data[0].performance
const columns = [
{
title: "Full Name",
children: [
{
title: "firstName",
dataIndex: "firstName",
key: "firstName"
},
{
title: "lastName",
dataIndex: "lastName",
key: "lastName"
}
]
},
{
title: "Performance",
dataIndex: "performance",
children:
subjects.map(e => {
return {
title: e.title,
dataIndex: "performance["+(e.id-1)+"].percentage",
key: "key-"+e.id,
render: value => <span>{value}%</span>
}
})
}
];

Because of the solution in answer from Mobeen does not work anymore, I have tried to solve this.
I have extended the render method for the children columns of performance column:
...
{
title: "Performance",
dataIndex: "performance",
children: subjects.map((assessment) => {
const { title, id } = assessment;
return {
title,
dataIndex: "performance",
key: id,
render: (values) =>
values.map((value, index) => {
let ret;
if (index === id - 1) ret = values[index].percentage + "%";
return ret;
})
};
})
}
...
It returns only the percentage value of the subject with the corresponding id.
It is not very clean, but it works.
Check the solution in sandbox: https://codesandbox.io/s/prod-lake-7n6zgj

Related

TableRow: Unexpected any. Specify a different type

Building a Table component using TS followed this to type everything as needed.
But as I broke down the row in it's separate component i am unable to type it properly.
export interface ITableRow<DataType> {
rowData: any; // The Issue is with this line
columnKeys: Array<DataType>;
}
function getTableRow<T>({ rowData, columnKeys }: ITableRow<T>) {
return columnKeys.map((columnKey, index) => (
<td key={index}>{rowData[columnKey]}</td>
));
}
export function TableRow<T>({ rowData, columnKeys }: ITableRow<T>) {
const tableRow = getTableRow({ rowData, columnKeys });
return <tr>{tableRow}</tr>;
}
The rowData prop is an object that can have any number of keys as specified in the headers object
for example:
const heads = [
{
id: 'company',
label: 'Company'
},
{
id: 'nb_employees',
label: 'Number of employees'
},
{
id: 'country',
label: 'Country'
}
];
const rows = [
{
company: 'Vody aho',
nb_employees: 1590,
country: 'Hong Kong'
},
{
company: 'Royal spirit',
nb_employees: 15,
country: 'USA'
},
];
How do I type the rowData dynamically in this case?
I tried any and it worked but then it's useless to use TS at all,
I tried extends as well but i don't think i understand it well so that failed as well

Search item in multidimensional array

I'm working with a tree nodes and I want to create a function to find an item by its ID.
What is the best and optimal solution to get it?
I think it will be a recursive function, but I'm not sure and I want a help please to choose what I will use to resolve this issue :)
This is my example:
const treeData = [
{
title: 'parent 1',
key: 11,
children: [
{
title: 'parent 1-0',
key: 12,
children: [
{
title: 'leaf',
key: 13,
children: [
{
title: 'leaf111',
key: 14,
},
{
title: 'leaf',
key: 15,
},
],
},
{
title: 'leaf666',
key:88,
},
],
},
{
title: 'parent 1-1',
key: 55,
children: [
{
title: (
<span
style={{
color: '#1890ff',
}}
>
sss
</span>
),
key: '0-0-1-0',
},
],
},
],
},
];
Input : 14
Output : {title: 'leaf111',key: 14},
We can create a rerusive function that:
Loops though each object in the array and
Returns the object if id matches
Calls itself with the objects children
const treeData = [{title: 'parent 1', key: 11, children: [{title: 'parent 1-0', key: 12, children: [{title: 'leaf', key: 13, children: [{title: 'leaf111', key: 14, }, {title: 'leaf', key: 15, }, ], }, {title: 'leaf666', key:88, }, ], }, {title: 'parent 1-1', key: 55, children: [{title: '(<span style={{color: \'#1890ff\', }} > sss </span> )', key: '0-0-1-0', }, ], }, ], }, ];
const findById = (e, id) => {
for (let o of e) {
return (o.key == id) ? o : findById(o.children, id);
}
}
const res = findById(treeData, 14);
console.log(res);
Output:
{
"title": "leaf111",
"key": 14
}
You can use a tree walker, which traverses the tree and calls a provided function upon visiting each node. The provided function could check to see if the node matches the provided ID.
An example:
function getNodeById(id) {
let matchingNode;
walk(tree, node => {
if(node.key === 14) {
matchingNode = node;
}
});
return matchingNode;
}
A tree walker can be implemented using recursion, as you mention. In a preorder operation, which starts at the topmost node, the walker takes the tree (the root node) and on each invocation:
calls the callback function with the current node
for each child node, calls itself with the child node and callback
function walker(node, cb) {
cb(node);
if (Array.isArray(node.children)) {
node.children.forEach(child => walker(child, cb));
}
}
For use with React, you can implement your own walking using React.Children.forEach, or you may prefer try a library like https://github.com/FormidableLabs/react-ssr-prepass.

React ant design table cell collapse issue

Im using my react type script project for ant design table
i want to know how to do that following image like as table, im search any tutorial but not seen anything, any one know how to do that correctly.
code sand box here
Thanks
image here
code here
class App extends React.Component {
columns: any = [
{
title: "Name",
dataIndex: "name",
key: "name"
},
{
title: "Age",
dataIndex: "age",
key: "age"
},
{
title: "Address",
dataIndex: "address",
key: "address"
},
{
title: "Tags",
key: "tags",
dataIndex: "tags"
},
{
title: "Action",
key: "action"
}
];
data: any = [
{
key: "1",
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park",
tags: ["nice", "developer"]
},
{
key: "2",
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park",
tags: ["loser"]
},
{
key: "3",
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park",
tags: ["cool", "teacher"]
}
];
render() {
return <Table columns={this.columns} dataSource={this.data} />;
}
}
You want to create 2 sub rows in each row, but only for some columns. you can use rowspan for this.
you can duplicate your rows (row1-row1-row2-row2-row3-row3-...), and put the subrow values in them (row1_subrow1-row1_subrow2-row2_subrow1-row2_subrow2-...), then use rowspan for the columns you want to expand (like Section and Name in your image), and expand the odd rows and collapse the even rows for this columns.
the full code : (Codesandbox Demo)
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Table } from "antd";
let multiRowRender = (value, row, index) => {
const obj = {
children: value,
props: {}
};
if (index % 2 === 0) {
obj.props.rowSpan = 2;
}
if (index % 2 === 1) {
obj.props.rowSpan = 0;
}
return obj;
};
const columns = [
{
title: "Number",
dataIndex: "number"
},
{
title: "Issue",
dataIndex: "issue"
},
{
title: "Name",
dataIndex: "name",
render: multiRowRender
},
{
title: "Section",
dataIndex: "section",
render: multiRowRender
}
];
let data = [
{
key: "1",
name: "John Brown",
issues: [32, 100],
numbers: [18889898989, 545054],
section: "sec 1"
},
{
key: "2",
name: "Jim Green",
issues: [42, 50],
numbers: [18889898888, 1420054],
section: "sec 2"
}
];
let data2 = [];
data.forEach(d => {
data2.push({ ...d, issue: d.issues[0], number: d.numbers[0] });
data2.push({
...d,
issue: d.issues[1],
number: d.numbers[1],
key: d.key + "-row2"
});
});
data = data2;
ReactDOM.render(
<Table columns={columns} dataSource={data} bordered />,
document.getElementById("container")
);
Codesandbox Demo

Filter Array based on a property in the array of its objects

Given is following data structure
const list = [
{
title: 'Section One',
data: [
{
title: 'Ay',
},
{
title: 'Bx',
},
{
title: 'By',
},
{
title: 'Cx',
},
],
},
{
title: 'Section Two',
data: [
{
title: 'Ay',
},
{
title: 'Bx',
},
{
title: 'By',
},
{
title: 'Cx',
},
],
},
];
What i want to do ist to filter this list based on title property in the data array of each object.
An example would be to have the list where the title property of the childs starts with "B", so the list will look like that:
const filteredList = [
{
title: 'Section One',
data: [
{
title: 'Bx',
},
{
title: 'By',
}
],
},
{
title: 'Section Two',
data: [
{
title: 'Bx',
},
{
title: 'By',
}
],
},
];
What i tried so far was something like that:
const items = list.filter(item =>
item.data.find(x => x.title.startsWith('A')),
);
or
const filtered = list.filter(childList => {
childList.data.filter(item => {
if (item.title.startsWith('B')) {
return item;
}
return childList;
});
});
But i think i am missing a major point here, maybe some of you could give me a tip or hint what i am doing wrong
Best regards
Your issue is that you're doing .filter() on list. This will either keep or remove your objects in list. However, in your case, you want to keep all objects in list and instead map them to a new object. To do this you can use .map(). This way you can map your objects in your list array to new objects which contain filtered data arrays. Here's an example of how you might do it:
const list=[{title:"Section One",data:[{title:"Ay"},{title:"Bx"},{title:"By"},{title:"Cx"}]},{title:"Section Two",data:[{title:"Ay"},{title:"Bx"},{title:"By"},{title:"Cx"}]}];
const filterByTitle = (search, arr) =>
arr.map(
({data, ...rest}) => ({
...rest,
data: data.filter(({title}) => title.startsWith(search))
})
);
console.log(filterByTitle('B', list));

Backbone-UI, TableView, rendering columns

I'm trying to render a table view with four columns, 'name', 'birthday', 'gender', 'married', but
a) they columns aren't showing up at all
b) I'm not even sure if I am passing them correctly, because when I console.log table.options the columns property is rendered as "empty":
Object {columns: Array[0], emptyContent: "no entries", onItemClick: function, sortable: false, onSort: null}
I've tried this:
var table = new Backbone.UI.TableView({
model: people,
columns: [
{ title: "Name", content: 'name' },
{ title: "Gender", content: "gender" } },
{ title: "Birthday", content: "birthday" } },
{ title: "Married", content: "married" } }
]
});
And this:
var table = new Backbone.UI.TableView({
model: people,
options: {
columns: [
{ title: "Name", content: 'name' },
{ title: "Gender", content: "gender" },
{ title: "Birthday", content: "birthday" },
{ title: "Married", content: "married" }
]
}
});
The source code change is adding the options as mu is too short said. Change the initialize method of the Backbone.UI.TableView object to be the following within the source code:
initialize : function(options) { //add parameter
Backbone.UI.CollectionView.prototype.initialize.call(this, arguments);
$(this.el).addClass('table_view');
this._sortState = {reverse : true};
this.options = _.extend({}, this.options, options); //Add this line
}
I'm sure there might be a better place to put this but I'm just going through tutorials to learn some advanced backbone stuff considering the documentation does not match the current version I would shy away from using the library in production as of right now. Hopefully it is fixed in the future.

Resources