Related
I'm trying to create a draggable table with antd, but i always use functionalcomponents in react and all the examples and doc that i found in internet is using class components and that=this stufs like. I don't know how can i use react-drag-listview library with the functional components.
this.state = {
columns: [
{
title: <span className="dragHandler">Key</span>,
dataIndex: "key",
render: (text) => <span>{text}</span>,
width: 50
},
{
title: <span className="dragHandler">Name</span>,
dataIndex: "name",
width: 200
},
{
title: <span className="dragHandler">Gender</span>,
dataIndex: "gender",
width: 100
},
{
title: <span className="dragHandler">Age</span>,
dataIndex: "age",
width: 100
},
{
title: <span className="dragHandler">Address</span>,
dataIndex: "address"
}
]
};
const that = this;
this.dragProps = {
onDragEnd(fromIndex, toIndex) {
const columns = [...that.state.columns];
const item = columns.splice(fromIndex, 1)[0];
columns.splice(toIndex, 0, item);
that.setState({
columns
});
},
nodeSelector: "th",
handleSelector: ".dragHandler",
ignoreSelector: "react-resizable-handle"
};
}
This a small piece of code that I'm trying to copy from, but i don't understand it.
Even an exaple of any small code where i can see how to use with functional components it may work for me.
Tis is the url of the example above: https://codesandbox.io/s/table-column-sortable-resizable-st9bt?file=/index.js
React Drag List View Using Functional Component
import "./index.css";
import { useState } from "react";
import { Resizable } from "react-resizable";
import { Table } from "antd";
import ReactDragListView from "react-drag-listview";
const ResizableTitle = (props) => {
const { onResize, width, ...restProps } = props;
if (!width) {
return <th {...restProps} />;
}
return (
<Resizable
width={width}
height={0}
handle={
<span
className='react-resizable-handle'
onClick={(e) => {
e.stopPropagation();
}}
/>
}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
>
<th {...restProps} />
</Resizable>
);
};
const data = [
{ key: "1", name: "Boran", gender: "male", age: "12", address: "New York" },
{ key: "2", name: "JayChou", gender: "male", age: "38", address: "TaiWan" },
{ key: "3", name: "Lee", gender: "female", age: "22", address: "BeiJing" },
{ key: "4", name: "ChouTan", gender: "male", age: "31", address: "HangZhou" },
{ key: "5", name: "AiTing", gender: "female", age: "22", address: "Xi’An" },
];
const components = { header: { cell: ResizableTitle } };
const App = () => {
const [columns, setColumns] = useState([
{
title: <span className='dragHandler'>Key</span>,
dataIndex: "key",
render: (text) => <span>{text}</span>,
width: 50,
},
{
title: <span className='dragHandler'>Name</span>,
dataIndex: "name",
width: 200,
},
{
title: <span className='dragHandler'>Gender</span>,
dataIndex: "gender",
width: 100,
},
{
title: <span className='dragHandler'>Age</span>,
dataIndex: "age",
width: 100,
},
{
title: <span className='dragHandler'>Address</span>,
dataIndex: "address",
},
]);
const onDragEnd = (fromIndex, toIndex) => {
setColumns((prev) => {
const nextColumns = [...prev];
const item = nextColumns.splice(fromIndex, 1)[0];
nextColumns.splice(toIndex, 0, item);
return nextColumns;
});
};
const handleResize = (size, index) => {
setColumns((prev) => {
const nextColumns = [...prev];
nextColumns[index] = {
...nextColumns[index],
width: size.width,
};
return nextColumns;
});
};
const tableColumns = columns.map((col, index) => ({
...col,
onHeaderCell: (column) => ({
width: column.width,
onResize: (e, { size }) => handleResize(size, index),
}),
}));
return (
<ReactDragListView.DragColumn
onDragEnd={onDragEnd}
nodeSelector='th'
handleSelector='.dragHandler'
ignoreSelector='react-resizable-handle'
>
<Table bordered columns={tableColumns} dataSource={data} components={components} />
</ReactDragListView.DragColumn>
);
};
export default App;
I can't get rid of the classic React "Each child in the list should have a unique key".
I'm rendering a big array, with items that can have sub items.
To prevent all of this, i created a data structure with unique id for every item.
This is my data:
export const assetListConfig: RolesListItem[] = [
{
id: 1,
title: t('Asset creation'),
name: 'Asset-Create',
type: 'switch',
},
{
id: 2,
title: t('Edit asset info'),
name: 'Asset-Edit',
type: 'switch',
},
{
id: 3,
title: t('Archive'),
name: 'Asset-Archive',
type: 'switch',
},
{
id: 4,
title: t('Delete'),
name: 'Asset-Delete',
type: 'switch',
subLists: [
{
subListWatch: 'Asset-Delete',
items: [
{
id: 5,
title: t('Bulk deletion'),
name: 'Asset-Delete-Bulk',
type: 'switch',
},
],
},
],
},
{
id: 6,
title: t('Resources'),
name: 'Asset-Manage-Resources',
type: 'switch',
},
{
id: 7,
title: t('Enable public'),
name: 'Asset-Set-Public',
type: 'switch',
},
{
id: 8,
title: t('Enable redirect'),
name: 'Asset-Set-Redirect',
type: 'switch',
},
{
id: 9,
title: t('Enable tickets'),
name: 'Asset-Enable-Tickets',
type: 'switch',
},
{
id: 10,
title: t('Edit hierarchy'),
name: 'Asset-Edit-Hierarchy',
type: 'switch',
itemWatch: ['Asset-Edit'],
},
{
id: 11,
title: t('Locations'),
name: 'Asset-Location',
type: 'switch',
},
{
id: 12,
title: t('Manage asset tags'),
name: 'Asset-Manage-Tags',
type: 'switch',
},
{
id: 13,
title: t('Logbook'),
name: 'Asset-Logbook-Read',
type: 'switch',
subLists: [
{
subListWatch: 'Asset-Logbook-Read',
items: [
{
id: 14,
title: t('Comment'),
name: 'Asset-Logbook-Comment',
type: 'switch',
},
{
id: 15,
title: t('Allow export'),
name: 'Asset-Logbook-Export',
type: 'switch',
},
],
},
],
},
{
id: 16,
title: t('Components'),
name: 'Component-Read',
type: 'switch',
subLists: [
{
subListWatch: 'Component-Read',
items: [
{
id: 17,
title: t('Instances'),
name: 'Component-Instance',
type: 'radio',
radioOptions: [
{ label: t('Manage'), value: 'Component-Instance-Manage' },
{
label: t('Delete'),
value: 'Component-Instance-Delete',
inputWatch: 'Component-Instance-Manage',
},
],
},
{
id: 18,
title: t('Models'),
name: 'Component-Model',
type: 'radio',
radioOptions: [
{
label: t('Manage'),
value: 'Component-Model-Manage',
inputWatch: 'Component-Instance-Manage',
},
{
label: t('Delete'),
value: 'Component-Model-Delete',
inputWatch: 'Component-Model-Manage',
},
],
},
],
},
],
},
{
id: 19,
title: t('Workshifts'),
name: 'Asset-Running-Read',
type: 'switch',
subLists: [
{
subListWatch: 'Asset-Running-Read',
items: [
{
id: 20,
title: t('Manage status'),
name: 'Asset-Running-Status-Manage',
type: 'switch',
},
{
id: 21,
title: t('Manage expectations'),
name: 'Asset-Running-Expectation-Manage',
type: 'switch',
},
{
id: 22,
title: t('Manage schedules'),
name: 'Asset-Running-Schedule-Manage',
type: 'switch',
},
],
},
],
},
];
I render lists with .map also for radiogroup options and item sublists.
This is the component in which I get the error:
import shortid from 'shortid'
export const UserRolesList: React.FC<ListItemProps> = ({
listConfig,
userData,
isSubList = false,
}) => {
handleChange ecc ecc...
return (
<RolesListWrapper isSubList={isSubList}>
<ul
className="list--unbulleted display--ib full-width"
>
/* FIRST LIST RENDERING */
**{listConfig.map((listItem, index) => {
const {
name,
title,
type,
radioOptions = [],
selectOptions = [],
id: skillId,
subLists,
} = listItem;**
const visibleOptions = renderVisibleOptions(radioOptions);
return (
<>
{isListItemVisible(listItem) ? (
**<li
className={`pad pad-double--sides shadow--list-item ${
index !== listConfig.length - 1 ? 'push--bottom' : ''
}`}
key={skillId}
style={
isSubList
? {
padding: '0',
boxShadow: 'none',
}
: {
width: 'auto',
minWidth: '350px',
}
}
>**
<div className="full-width flex--space-items">
<div
className="pad flex--align-center"
style={{
paddingLeft: isSubList ? 'var(--base-space)' : '',
flexBasis: '160px',
}}
>
{title}
</div>
{type === 'switch' ? (
<div className="pad flex--align-center">
<Switch
onColor="#21b151"
offColor="#e02f2f"
checked={findActiveSkill(name)}
onChange={() => handleSkillChange(name)}
disabled={isAddingSkill || isRemovingSkill}
/>
</div>
) : null}
{type === 'radio' ? (
**<div
className="flex-item--fill"
style={{
display: 'grid',
gridTemplateColumns: 'repeat(3,1fr)',
}}
>
/* SECOND LIST RENDERING */
{visibleOptions.map((opt) => (
<div
className="flex--align-center"
style={{
gridColumn: opt.value.includes('Delete')
? '3'
: undefined,
}}
key={shortid.generate()}
>**
<input
type="checkbox"
className="qtrack-form-input__selection"
value={opt.value}
id={opt.value}
checked={findActiveSkill(opt.value)}
onChange={(e) =>
handleSkillChange(e.target.value)
}
disabled={isAddingSkill || isRemovingSkill}
/>
<label
style={{ margin: '5px 0' }}
htmlFor={opt.value}
>
{opt.label}
</label>
</div>
))}
</div>
) : null}
{type === 'select' ? (
<div style={{ margin: '0', minWidth: '220px' }}>
<SimpleSelect
selectOptions={selectOptions}
value={selectOptions.find((opt) =>
skills.includes(opt.value),
)}
isMulti={false}
isClearable
errors={undefined}
onChange={(e: string) => handleSelectChange(e, name)}
name="roles-select"
isDisabled={isAddingSkill || isRemovingSkill}
/>
</div>
) : null}
</div>
{subLists ? (
<SubListHandler subLists={subLists} userData={userData} />
) : null}
</li>
) : null}
</>
);
})}
</ul>
</RolesListWrapper>
);
};
And this is the component in which I render the sublists:
export const SubListHandler: React.FC<{
subLists: RolesList[];
userData: UserData;
}> = ({ subLists, userData }) => (
<>
/* THIRD AND LAST LIST RENDER */
{subLists.map((list) => {
const { subListWatch, items: subItems } = list;
const isSubListVisible = userData.skills.includes(subListWatch);
**const subListId = shortid.generate();**
return (
<div key={subListId}>
{isSubListVisible ? (
**<div>**
{list.title ? (
<div className="txt--bold pad--left push--top">
{list.title}
</div>
) : null}
<div className="push--ends pad--left">
<UserRolesList
listConfig={subItems}
userData={userData}
isSubList
/>
</div>
</div>
) : null}
</>
);
})}
</>
);
As you can see, I'm using unique IDs for all the three map methods in my component.
This is confirmed when I log the IDs I used in the console. They're all different.
I don't know what I'm doing wrong or if I'm messing something with the conditional rendering or nested components
I tried to highlight when the list rendering happens in the code.
This seems pretty straighforward to me as I have unique ids for every list child, but it always throws me an error.
Let me know if you find something wrong with my code.
Thanks a lot!
You need to use the unique id in the 'key' prop of the list items. ie. -
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
I want to add buttons in each row of the material-ui data grid... But when I'm trying to do so, I see the output as follows (rather than the button it shows something like [object Object].
enter image description here
Below is my code:
import { DataGrid } from "#mui/x-data-grid";
import React, { useState } from "react";
function App() {
const columns = [
{ field: "id", headerName: "ID", width: 70 },
{ field: "firstName", headerName: "First name", width: 130 },
{ field: "lastName", headerName: "Last name", width: 130 },
{
field: "age",
headerName: "Age",
type: "number",
width: 90,
},
{
field: "fullName",
headerName: "Full name",
description: "This column has a value getter and is not sortable.",
sortable: false,
width: 160,
valueGetter: (params) =>
`${params.getValue(params.id, "firstName") || ""} ${
params.getValue(params.id, "lastName") || ""
}`,
},
{
field: "click",
headerName: "Click",
type: <button />, /*this field stores the buttons*/
width: 90,
},
];
const [rows, setRows] = useState([
{
id: 1,
lastName: "Snow",
firstName: "<a>hello</a>",
age: 35,
click: <button>Hello</button>, /*given a button to first row*/
},
{ id: 2, lastName: "Lannister", firstName: "Cersei", age: 42 },
{ id: 3, lastName: "Lannister", firstName: "Jaime", age: 45 },
{ id: 4, lastName: "Stark", firstName: "Arya", age: 16 },
{ id: 5, lastName: "Targaryen", firstName: "Daenerys", age: null },
{ id: 6, lastName: "Melisandre", firstName: null, age: 150 },
{ id: 7, lastName: "Clifford", firstName: "Ferrara", age: 44 },
{ id: 8, lastName: "Frances", firstName: "Rossini", age: 36 },
{ id: 9, lastName: "Roxie", firstName: "Harvey", age: 65 },
]);
// setRows(
// setRows(rows);
// );
const [count, setCount] = useState(0);
const [arr, setArr] = useState([1, 2, 3]);
return (
<>
<p>you pressed {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<div style={{ height: 400, width: "50%" }}>
<DataGrid
rows={rows}
columns={columns}
pageSize={5}
rowsPerPageOptions={[5]}
checkboxSelection
/>
</div>
<table>
{arr.map((n) => (
<tr>${n}</tr>
))}
</table>
<button
onClick={(e) => {
e.preventDefault();
arr.push(4);
console.log(arr);
var t = [...rows, { id: 3 }];
// rows.push({ lastName: "hello" });
// setRows(t);
setRows(t);
console.log(rows);
// document.title = arr;
setArr(arr);
}}
>
add
</button>
</>
);
}
export default App;
Also, after the button is inserted, how can I reference the current index or id of the row inside the button, suppose if at every click of button I need to show an alert which displays the information such as id and firstname for that row... how can i do that?
use render cell:
{
field: "click",
headerName: "Click",
width: 90,
renderCell: (params) => {
// you will find row info in params
<button>Click</button>
}
},
I have a table from Ant Design where i want to sort a column. This column is name.
const data = [
{
key: "1",
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park"
},
{
key: "2",
name: "Carl Green",
age: 42,
address: "London No. 1 Lake Park"
},
{
key: "3",
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park"
},
{
key: "4",
name: "Jim Red",
age: 32,
address: "London No. 2 Lake Park"
}
];
class App extends React.Component {
state = {
filteredInfo: null,
sortedInfo: null
};
handleChange = (pagination, filters, sorter) => {
console.log("Various parameters", pagination, filters, sorter);
this.setState({
filteredInfo: filters,
sortedInfo: sorter
});
};
clearFilters = () => {
this.setState({ filteredInfo: null });
};
clearAll = () => {
this.setState({
filteredInfo: null,
sortedInfo: null
});
};
setAgeSort = () => {
this.setState({
sortedInfo: {
order: "descend",
columnKey: "age"
}
});
};
render() {
let { sortedInfo } = this.state;
sortedInfo = sortedInfo || {};
const columns = [
{
title: "Name",
dataIndex: "name",
key: "name",
sorter: (a, b) => a.name.length - b.name.length,
sortOrder: sortedInfo.columnKey === "name" && sortedInfo.order,
ellipsis: true
},
{
title: "Age",
dataIndex: "age",
key: "age",
sorter: (a, b) => a.age - b.age,
sortOrder: sortedInfo.columnKey === "age" && sortedInfo.order,
ellipsis: true
},
{
title: "Address",
dataIndex: "address",
key: "address",
sorter: (a, b) => a.address.length - b.address.length,
sortOrder: sortedInfo.columnKey === "address" && sortedInfo.order,
ellipsis: true
}
];
return (
<>
<Table
columns={columns}
dataSource={data}
onChange={this.handleChange}
/>
</>
);
}
}
Now, when i sort in ascending order the data does not order in the right way. Why? And how to solve?
demo: https://codesandbox.io/s/reset-filters-and-sorters-antd494-forked-m3ljo?file=/index.js:143-2169
Your sort compare function is comparing the length of name which will sort the array by the length of the name.
Change your sorter for name to below.
sorter: (a, b) => {
if(a.name < b.name) { return -1; }
if(a.name > b.name) { return 1; }
return 0;
},
You also need to change sorter for address.
sorter: (a, b) => {
if(a.address < b.address) { return -1; }
if(a.address > b.address) { return 1; }
return 0;
},
Try this sorter function for name:
sorter: (a, b) => a.name.toUpperCase().localeCompare(b.name.toUpperCase())
I've Tried this code on your snippet and it is working good -
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Table } from "antd";
const data = [
{
key: "1",
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park"
},
{
key: "2",
name: "Carl Green",
age: 42,
address: "London No. 1 Lake Park"
},
{
key: "3",
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park"
},
{
key: "4",
name: "Jim Red",
age: 32,
address: "London No. 2 Lake Park"
}
];
class App extends React.Component {
state = {
filteredInfo: null,
sortedInfo: null
};
handleChange = (pagination, filters, sorter) => {
console.log("Various parameters", pagination, filters, sorter);
this.setState({
filteredInfo: filters,
sortedInfo: sorter
});
};
clearFilters = () => {
this.setState({ filteredInfo: null });
};
clearAll = () => {
this.setState({
filteredInfo: null,
sortedInfo: null
});
};
setAgeSort = () => {
this.setState({
sortedInfo: {
order: "descend",
columnKey: "age"
}
});
};
render() {
let { sortedInfo } = this.state;
sortedInfo = sortedInfo || {};
const columns = [
{
title: "Name",
dataIndex: "name",
key: "name",
sorter: (a,b) => (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0),
sortOrder: sortedInfo.columnKey === "name" && sortedInfo.order,
ellipsis: true
},
{
title: "Age",
dataIndex: "age",
key: "age",
sorter: (a, b) => a.age - b.age,
sortOrder: sortedInfo.columnKey === "age" && sortedInfo.order,
ellipsis: true
},
{
title: "Address",
dataIndex: "address",
key: "address",
sorter: (a,b) => (a.address > b.address) ? 1 : ((b.address > a.address) ? -1 : 0),
sortOrder: sortedInfo.columnKey === "address" && sortedInfo.order,
ellipsis: true
}
];
return (
<>
<Table
columns={columns}
dataSource={data}
onChange={this.handleChange}
/>
</>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
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