How to create multi editable cells on click in React-table - reactjs

I have a table with 4 columns: First Name, Last Name, City, Address. If the user click on City, City and Address becomes editable.
class Home extends React.PureComponent {
constructor(props){
super(props);
this.state = {
columns : [
{
Header: 'First Name',
accessor: 'firstName'
},
{
Header: 'Last Name',
accessor: 'lastName'
},
{
Header: 'City',
accessor: 'city',
Cell: this.clickableCell
},
{
Header: 'Address',
accessor: 'addr'
}
],
data: [
{
firstName: "MyName",
lastName: "MyLastName",
city: "MyCity",
addr: "My Address"
}
],
};
}
renderEditable = (cellInfo) => {
const products = this.state.data;
const row = products[cellInfo.index];
const value = row[cellInfo.column.id]
return (
<div contentEditable suppressContentEditableWarning>
{value}
</div>
)
};
clickableCell = (cellInfo) => {
const products = this.state.data;
const row = products[cellInfo.index];
const value = row[cellInfo.column.id]
const onClick = () => {
let c = Object.assign({}, this.state);
// Change the City and Address Cell's function to create an
// editable cell
c.columns[2]['Cell'] = this.renderEditable;
c.columns[3]['Cell'] = this.renderEditable;
this.setState({...c});
this.forceUpdate();
}
return (
<div onClick={onClick}>
{value}
</div>
)
};
render() {
return(
<ReactTable
className="-striped -highlight"
data={this.state.data}
columns={this.state.columns}
/>
)
}
}
My idea was bind to City's Cell a function to create a clickable div and after replace the Cell's function with a different function to render an editable div..but this idea doesn't work...

Related

How can I have a button in a const object?

I'm learning React.js and this is a table showing which user has which items.
I would like to have a button for each item and delete the corresponding item.
How do you have or {FaTrash} icon in a const object?
This is my full code below
const columns = [
{
name: "Username",
selector: "username",
sortable: true
},
{
name: "Email",
selector: "email",
sortable: true
},
{
name: "Item",
selector: "items",
sortable: true,
right: true
},
{
name: "Action",
value: <button>Edit</button>
}
]
const Admin = () => {
const [data, setData] = useState(allUsers);
const handleRowClicked = row => {
const updatedData = data.map(item => {
if (row.id !== item.id) {
return item;
}
return {
...item,
toggleSelected: !item.toggleSelected
};
});
setData(updatedData);
}
return ( <>
<div className='users p-5'>
<DataTable
title="Users"
columns={columns}
data={data}
defaultSortField="title"
pagination
onRowClicked={handleRowClicked}
/>
</div>
</> );
}
export default Admin;
I used to pass a function that returns a piece of layout with handler
{
name: "Action",
actionRenderer: ({ index, item }) => {
return (
<button onClick={() => onhandle(item)}>
ActionName <!--or icon component-->
</button>
)
}
},
Than you need to create <DataTableRow> component wich will render each object in your columns array. Somewhere in the <DataTableRow> you will be able to access to actionRenderer and your data item:
<div>{actionColumn.actionRenderer({ index, item })}</div>

Ant design Transform component extracting selected data

I need to extract selected/filtered data into the state. I tried everything with onChange and on select change but only I got is the ID of one specific item.
ex. extracted_data = [ '1','2','3'....]
But I need a complete Item with id, name, price, etc...
I am passing data as source data, and after selecting I want to send in the parent component because I need to send it on the server.
Code is below
import { Table, Transfer } from "antd";
import difference from "lodash/difference";
import React, { useState } from "react";
// Customize Table Transfer
const TableTransfer = ({ leftColumns, rightColumns, ...restProps }) => (
<Transfer {...restProps}>
{({
direction,
filteredItems,
onItemSelectAll,
onItemSelect,
selectedKeys: listSelectedKeys,
disabled: listDisabled,
}) => {
const columns = direction === "left" ? leftColumns : rightColumns;
const rowSelection = {
getCheckboxProps: (item) => ({
disabled: listDisabled || item.disabled,
}),
onSelectAll(selected, selectedRows) {
const treeSelectedKeys = selectedRows
.filter((item) => !item.disabled)
.map(({ key }) => key);
const diffKeys = selected
? difference(treeSelectedKeys, listSelectedKeys)
: difference(listSelectedKeys, treeSelectedKeys);
onItemSelectAll(diffKeys, selected);
},
onSelect({ key }, selected) {
onItemSelect(key, selected);
},
selectedRowKeys: listSelectedKeys,
};
return (
<Table
rowSelection={rowSelection}
columns={columns}
dataSource={filteredItems}
size="small"
style={{
pointerEvents: listDisabled ? "none" : undefined,
}}
onRow={({ key, disabled: itemDisabled }) => ({
onClick: () => {
if (itemDisabled || listDisabled) return;
onItemSelect(key, !listSelectedKeys.includes(key));
},
})}
/>
);
}}
</Transfer>
);
const leftTableColumns = [
{
dataIndex: "name",
title: "Name",
},
{
dataIndex: "price",
title: "Price (€)",
},
{
dataIndex: "discount",
title: "Discount (%)",
},
];
const rightTableColumns = [
{
dataIndex: "name",
title: "Name",
},
{
dataIndex: "price",
title: "Price",
},
];
const App = ({ data, func }) => {
const mockData = data.map((item) => ({
key: item.id.toString(),
name: item.name,
price: item.price,
discount: item.discount,
}));
const originTargetKeys = mockData.map((item) => item.key);
const [targetKeys, setTargetKeys] = useState(originTargetKeys);
const [selected, setSelected] = useState([]);
const onSelectChange = (e) => {
setSelected(e);
};
const onChange = (e) => {
setTargetKeys(e);
func(selected);
};
return (
<>
<TableTransfer
dataSource={mockData}
targetKeys={targetKeys}
disabled={false}
showSearch={true}
onChange={onChange}
onSelectChange={onSelectChange}
filterOption={(inputValue, item) =>
item.name.indexOf(inputValue) !== -1
}
leftColumns={leftTableColumns}
rightColumns={rightTableColumns}
/>
</>
);
};
export default App;

React setState not rendering view of bootstrap table

I have this component that has a Bootstrap Table and a Modal. User should be able to go into the modal and change the state of the same data for both the table and the modal; however, I am seeing that it is only changing the view in the modal but not the table?
Component with Table and Modal:
export class TableAndModal extends React.Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
showModal: false,
index: ""
}
};
this.setShow = this.setShow.bind(this);
this.handleShowAndChange = this.handleShowAndChange.bind(this);
}
columns = [{
dataField: "view",
text: "View"
formatter: (cell, row, rowIndex) => {
return (
<div>
<Button variant="info" onClick={() => this.setShow(rowIndex)}>View</Button>
</div>
);
}
},
{dataField: 'fullName', text: 'Full Name' },
{dataField: 'studentDesc', text: 'Student Description'},
{dataField: 'email', text: 'Email'},
{dataField: 'fullNotes', text: 'Full Notes'},
{dataField: 'edu', text: 'Education'},
{dataField: 'phone', text: 'Phone Number'},
{dataField: 'id', text: 'ID'}];
setShow(index) {
this.setState({
showModal: true,
index: index
});
}
handleShowAndChange = (name, value) => {
this.setState((prevState) => {
let newState = {...prevState};
newState[name] = value;
return newState;
});
};
render() {
return (
<div>
<BootstrapTable
hover
condensed={true}
bootstrap4={true}
keyField={'id'}
data={this.state.data.info}
columns={this.columns}
/>
<Modal data={this.state.data.info} index={this.state.index}
showModal={this.state.showModal} onChange={this.handleShowAndChange} />
</div>
);
}
}
Modal:
this.state = {
data: this.props.data
};
handleInfoChange = (index) => (name, value) => {
let info = this.state.data.info.slice();
info[index][name] = value;
this.props.onChange("info", info);
};
I am seeing that the state is being modified correctly. However, the table still has the same view with the old state data even though the state has been changed.
Can someone guide me on what I am doing wrong?
I think you should use props.data instead of this.props.data
constructor(props) {
super(props);
this.state = {
data: props.data,
showModal: false,
index: ""
}
}

Getting error while showing hiding React Table columns - React JS

I am working on React Table. I am basically a beginner in React. I have a dashboard page where I display a React Table of 8 columns. I have a customize button which will open a popup page, this popup page has 8 check boxes allows me to show/hide those React columns. Initially all the check boxes in this popup page is set to true. When I uncheck a column that particular column get disabled.
There are images in the end to see what I am trying to do.
I will be using this logic for show hide columns (this question was asked by me two days back) -
How to show and hide some columns on React Table?
The React Table data is like this
const columns = [
{
Header: 'Column 1',
accessor: 'firstName',
},
{
Header: 'Column 2',
accessor: 'firstName',
},
{
Header: 'Column 3',
accessor: 'firstName',
},
{
Header: 'Column 4',
accessor: 'firstName',
},
{
Header: 'Column 5',
accessor: 'firstName',
},
{
Header: 'Column 6',
accessor: 'firstName',
},
{
Header: 'Column 7',
accessor: 'firstName'
},
{
Header: 'Column 8',
accessor: 'firstName',
}
];
The start of the dashboard page
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
filterState: {},
searchText: '',
isFilterOpen: false,
isCustomizedOpen: false,
isFiltered: false,
isSearched: false,
searchedTableData: [],
filteredTableData: [],
};
this.handleCustClickinv = this.handleCustClickinv.bind(this);
}
This is my code in the render function of my dashboard page for showing the customize button (this is written in parent dashboard page)
{this.state.isCustomizedOpen && <CustomizedView
filterState={this.state.filterState}
applyFilter={(values, clear) => { this.applyFilters(values, clear); }}
/>}
This is the code for the customize button (this is written in parent dashboard page)
<div className="custom-div-dashboard" onClick={() => { this.handleCustClickinv(); }}>
<div className='customize-view-dashboard'>Customized View </div>
</div>
This is function to handle the click on customize button (this is written in parent dashboard page)
handleFilterClickinv() {
if(this.state.isCustomizedOpen) {
this.setState({ isCustomizedOpen: false });
}
const currentState = this.state.isFilterOpen;
this.setState({ isFilterOpen: !currentState });
}
This is my entire popup page which will have 8 check boxes
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { ActionCreators } from '../../../actions';
import './enqCustomizedView.scss';
import ButtonComponent from '../../shared/button/ButtonComponent';
import { CheckBox } from '../../shared/chkbox/CheckBox';
class CustomizedView extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ id: 1, value: 'Column 1', isChecked: true },
{ id: 2, value: 'Column 2', isChecked: true },
{ id: 3, value: 'Column 3', isChecked: true },
{ id: 4, value: 'Column 4', isChecked: true },
{ id: 5, value: 'Column 5', isChecked: true },
{ id: 6, value: 'Column 6', isChecked: true },
{ id: 7, value: 'Column 7', isChecked: true },
{ id: 8, value: 'Column 8', isChecked: true },
]
};
this.handleCheckChildElement = this.handleCheckChildElement.bind(this);
}
handleClick() {
this.setState({ isChecked: !this.state.isChecked });
}
handleCheckChildElement(event) {
//let items = this.state.items;
let { items } = this.state;
items.forEach(items = () => {
if(items.value === event.target.value) {
items.isChecked = event.target.checked;
}
});
this.setState({ items });
const column1checked = items[0].isChecked;
console.log('column1checked ' + column1checked);
const column2checked = items[1].isChecked;
console.log('column2checked ' + column2checked);
const column3checked = items[2].isChecked;
console.log('column3checked ' + column3checked);
const column4checked = items[3].isChecked;
console.log('column4checked ' + column4checked);
const column5checked = items[4].isChecked;
console.log('column5checked ' + column5checked);
const column6checked = items[5].isChecked;
console.log('column6checked ' + column6checked);
const column7checked = items[6].isChecked;
console.log('column7checked ' + column7checked);
const column8checked = items[7].isChecked;
console.log('column8checked ' + column8checked);
}
render() {
return (
<div className='popup-page-custom' >
<div className='bottomBar'>
<ButtonComponent
text='Apply'
className='activeButton filterMargin'
width='100'
display='inline-block'
onClick={() => { this.props.applyFilter(this.state, false); }}
/>
<ButtonComponent
text='Clear Filter'
className='greyedButton clear-filter'
width='100'
display='block'
marginTop='60'
onClick={() => { this.props.applyFilter(this.state, true); }}
/>
</div>
<div>
<div className='data-points-text'>
<span> Columns </span>
</div>
<div className="App">
<ul>
{
this.state.items.map((item, i) => {
return (<div key={i} ><CheckBox handleCheckChildElement={this.handleCheckChildElement} {...item} /></div>);
})
}
</ul>
</div>
</div>
</div>
);
}
}
CustomizedView.propTypes = {
applyFilter: PropTypes.func.isRequired
};
CustomizedView.defaultProps = {
};
function mapStateToProps(state) {
return {
auth: state.auth
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(CustomizedView);
And ultimately this is my checkbox page
import React from 'react';
import PropTypes from 'prop-types';
export const CheckBox = (props) => {
// super(props);
return (
<li>
<input key={props.id} onClick={props.handleCheckChildElement} type="checkbox" checked={props.isChecked} value={props.value} /> {props.value}
</li>
);
};
CheckBox.propTypes = {
id: PropTypes.string,
handleCheckChildElement: PropTypes.func,
isChecked: PropTypes.bool,
value: PropTypes.string,
};
CheckBox.defaultProps = {
id: '',
handleCheckChildElement: null,
isChecked: null,
value: '',
};
export default CheckBox;
This is a very basic (ugly) style of my dashboard page and popup page
This is the error I am getting on Chrome when unchecking the checkboxes
Edit 1 - As per Alireza Yadegari's suggestion, I made a 1 line change. But I am still getting 2 errors.
Edit 2 - As per Alireza Yadegari's suggestion, I applied console.
you have to use this piece of code in your constructor
this.handleCheckChildElement = this.handleCheckChildElement.bind(this)
let { items } = { ...this.state };
this is wrong ....
firstly you destructuring array to object then saying give me items prop from given object... of course this is wrong
const { items} = this.state;
takes items prop from the state
and finally.... implement your task with foreach is bad idea...
CheckBox.defaultProps = {
id: '',
handleCheckChildElement: null,
isChecked: null, value: '',
};
i don't understand what it does. you know?
I think your project is a sample and no need for further examples.
I just say about your mistakes.
first, when you are using methods it is good to use 'bind(this)' to show react where is the method belongs.
secondly, when you are using state, react just allows you to change it in the constructor and wherever you want to change it you have to use 'setState' method (you can read the reason for this in react documentation).
finally, if you have an array in your state you have to get an array in some temp object change the temp object and then apply changes with 'setState' method. if you have more question please feel free to ask.

Ant Design - Collapse expanded rows on click of pagination buttons

I am implementing the expandable row feature on an ant design table (Expandable Row), and it works perfectly fine as stated on the ant design site. But I would like to expand the functionality of the table to include collapsing of the rows when the user clicks on the buttons to the lower right of the table that allow pagination. This is a fairly straightforward question so I won't clutter it by posting code. Any help or links would be greatly appreciated.
EDIT Code snippet
import * as React from 'react';
import { Tooltip, Table } from 'antd';
import * as IAssignmentsResponse from '../../interfaces/QC/IAssignmentResponse';
import * as moment from 'moment';
const expandedRowRender = (rowData) => {
const columns = [
{ title: 'Row1', dataIndex: 'Row1DataIndex', key: '1'},
{ title: 'Row2', dataIndex: 'Row2DataIndex', key: '2'},
{ title: 'Row3', dataIndex: 'Row3DataIndex', key: '3'},
];
return <Table
columns={columns}
dataSource={rowData.DataArray}
pagination={false}>
</Table>
}
const bindRows = (row) => {
return row.Workitem.WorkflowRefID;
}
const columns = [
{
title: 'MasterRow1',
dataIndex: 'MasterRow1DataIndex',
key: '1',
render(value) { return value.WorkflowRefID; },
onFilter: (value, record) => record.Workitem.data1.indexOf(value) === 0,
sorter: (a, b) => a.Workitem.data1 - b.Workitem.data1
},
{
title: 'MasterRow2',
dataIndex: 'MasterRow1DataIndex',
key: '2',
render(value, record) { return <Tooltip title={record.data2} mouseEnterDelay={.5}>{value}</Tooltip> },
onFilter: (value, record) => record.data2.indexOf(value) === 0,
sorter: (a, b) => a.data2- b.data2
},
{
title: 'MasterRow3',
dataIndex: 'MasterRow1DataIndex',
key: '3',
render(value, record) { return <Tooltip title={record.data3} mouseEnterDelay={.5}>{value}</Tooltip> },
onFilter: (value, record) => record.data3.indexOf(value) === 0,
sorter: (a, b) => a.data3- b.data3
}
]
return <Table rowKey={record => bindRows(record)}
columns={columns}
dataSource={this.props.assignmentData.AssignmentsResponse.Assignment}
expandedRowRender={record => expandedRowRender(record)}
onExpand={this.onTableRowExpand}
/>
You can achieve this using the expandedRowKeys method of the Ant Design's Table component's API, which is the list of keys of already expanded rows. An empty list means all rows are collapsed.
You can capture pagination button clicks using the onChange method, which can be used to call a function that sets the state of your component:
class MyTable extends Component {
constructor(props) {
super(props);
this.state = { currentPage: 1, expandedRows: [] };
this.handleChange = this.handleChange.bind(this);
}
handleChange(pagination) {
// changes the page, collapses all rows
this.setState({ currentPage: pagination.current, expandedRows: [] });
}
handleRowExpand(record) {
// if a row is expanded, collapses it, otherwise expands it
this.setState(prevState =>
prevState.expandedRows.includes(record.key)
? {
expandedRows: prevState.expandedRows.filter(
key => key !== record.key
)
}
: { expandedRows: [...prevState.expandedRows, record.key] }
);
}
render() {
return (
<Table
dataSource={YOUR_DATA}
...OTHER_PROPS
pagination={{ current: this.state.currentPage }}
// pagination buttons clicked
onChange={this.handleChange}
// expand + icon clicked
onExpand={(expanded, record) => this.handleRowExpand(record)}
// tell the 'Table' component which rows are expanded
expandedRowKeys={this.state.expandedRows}
/>
);
}
}

Resources