Closing a modal popping up through a react table - reactjs

I am popping up a modal an edit data through a button-click in the react table.
After performing the function when I try to close the modal, it would navigate to an empty page. I am calling the modal in the parent component from a child component as folllows.
<EditCardModal
show={this.state.show}
onHide={this.closeModal}
title="EDIT CARD"
id={this.state.id}
name={this.state.name}
uid={this.state.uid}
status={this.state.status}
serial={this.state.serial}
nodeType={this.state.nodeType}
onChange={this.handleChange}
handleEdit={this.handleEdit}
msg={this.state.msg}
/>
In the child component a close button is added to the modal header as below.
<Modal.Header closeButton>
<Modal.Title>{this.props.title}</Modal.Title>
</Modal.Header>
The method below shows how I close the modal.
closeModal = async event => {
event.preventDefault();
await this.setState({
show: false
});
};
TableList.js
class TableList extends Component {
constructor(props) {
super(props);
this.state = { show: false };
this.nodeCreationData = {};
this.state = {
show: false,
excelData: null,
rowInfo: null,
name: "",
uid: "",
order: "",
serial: "",
status: "",
excelDataValidation: false,
nodeType: "",
isSuccess: false,
a: null,
msg: "",
title: ""
};
this.showModal = this.showModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
showModal() {
// event.preventDefault();
this.setState({
show: true
});
}
closeModal() {
// event.preventDefault();
debugger;
this.setState({
show: false
});
}
componentWillMount() {
this.props.queryStart();
this.props.getCard();
}
getTdProps = (state, rowInfo, column, instance) => {
return {
onClick: async (e, handleOriginal) => {
console.log("It was in this row:", rowInfo.original.name);
await this.setState({
id: rowInfo.original.id,
name: rowInfo.original.name,
uid: rowInfo.original.uid,
serial: rowInfo.original.serial,
status: rowInfo.original.status,
nodeType: rowInfo.original.nodeType
});
if (handleOriginal) {
handleOriginal();
}
console.log("State", this.state);
}
};
};
tableHeaders = [
{
Header: "ID",
accessor: "id"
},
{
Header: "NAME",
accessor: "name"
},
{
Header: "UID",
accessor: "uid"
},
{
Header: "STATUS",
accessor: "status"
},
{
Header: "SERIAL",
accessor: "serial"
},
{
Header: "UPDATED_AT",
accessor: "updatedAt"
},
{
Header: "NODE TYPE",
accessor: "nodeType"
},
{
Header: "ACTION",
accessor: "action",
minWidth: 150,
Cell: ({ row }) => (
<div>
<Button type="submit" onClick={this.showModal}>
UPDATE CARD
</Button>
</div>
)
}
];
handleEdit = () => {
this.props.editCard({
id: this.state.id,
createdAt: this.state.createdAt,
name: this.state.name,
uid: this.state.uid,
serial: this.state.serial,
name: this.state.name,
status: this.state.status,
nodeType: this.state.nodeType
});
};
handleChange = event => {
if (event.target.name == "id") {
this.setState({
id: event.target.value
});
}
if (event.target.name == "nodeType") {
this.setState({
nodeType: event.target.value
});
}
if (event.target.name == "name") {
this.setState({
name: event.target.value
});
}
if (event.target.name == "serial") {
this.setState({
serail: event.target.value
});
}
if (event.target.name == "uid") {
this.setState({
uid: event.target.value
});
}
if (event.target.name == "status") {
this.setState({
status: event.target.value
});
}
};
render() {
const { TableListData, TableListLoading } = this.props;
var nodeTableList = null;
if (TableListData == null) {
nodeTableList = "No data";
} else {
nodeTableList = (
<ReactTable
// ref={r => {
// this.reactTable = r;
// }}
data={TableListData[0]}
columns={this.tableHeaders}
getTdProps={this.getTdProps}
/>
);
}
let loadingMsg = TableListLoading && <Alert>Loading</Alert>; //Show loading message
return (
<Grid fluid>
<EditCardModal
show={this.state.show}
onHide={this.closeModal}
title="EDIT CARD"
id={this.state.id}
name={this.state.name}
uid={this.state.uid}
status={this.state.status}
serial={this.state.serial}
nodeType={this.state.nodeType}
onChange={this.handleChange}
handleEdit={this.handleEdit}
msg={this.state.msg}
/>
<Row>
<Col xs={12} md={12}>
{loadingMsg}
{nodeTableList}
</Col>
</Row>
</Grid>
);
}
}
function mapStateToProps(state) {
return {
TableListData: state.cardUploader.data
};
}
export default withRouter(
connect(
mapStateToProps,
cardUploaderActions
)(TableList)
);
Can someone help me to solving this matter?

Related

How to post mongoose objectId using axios to the frontend react-select from the backend

I have a simple backend code which works well with Rest API
updated code
ClientSchema.js
const mongoose = require('mongoose');
var Schema = mongoose.Schema;
const customerSchema = new Schema({
paymentReferenceCode:{
type: String,
required: true
},
paymentType:{
type: String,
required: true
},
paymentDescription:{
type: String,
required: true
},
procedureAmount:{
type: String,
required: true
},
paymentDiscount:{
type: Number,
required: true
},
AmountPaid:{
type: Number,
required: true
},
Total:{
type: Number,
required: true
},
clientdetails: {
ref: 'Client',
type: mongoose.Schema.Types.ObjectId
},
}, { timestamps: true })
module.exports = mongoose.model('Customer',customerSchema);
Client.js
const mongoose = require('mongoose');
const ClientSchema = new mongoose.Schema({
fullName:{
type:String,
required: true
},
dateofBirth:{
type:Date,
required: true
},
gender:{
type:String,
required: true
},
nationality:{
type:String,
required: true
},
address:{
type:String,
required: true
},
date:{
type:Date,
default:Date.now
}
});
module.exports = mongoose.model('Client', ClientSchema)
Router.js
router.post("/addbill",async(req,res)=>{
try {
console.log(req.params);
const clientid = await clients.findOne({ _id: req.params.id });
await new Customer({
clientdetails:clientid,
paymentReferenceCode:req.body.paymentReferenceCode,
paymentType:req.body.paymentType,
paymentDescription:req.body.paymentDescription,
procedureAmount:req.body.procedureAmount,
paymentDiscount:req.body.paymentDiscount,
AmountPaid:req.body.AmountPaid,
Total:req.body.Total
}).save(async (err, data) => {
if (err) {
console.log('err:', err);
res.status(500).json({
message: 'Something went wrong, please try again later.'
});
} else {
res.status(200).json({
message: 'Bill Created',
data,
id: data._id
});
}
});
} catch (error) {
res.status(422).json(error);
}
})
router.get('/', async (req, res) => {
try{
const data = await clients.find();
res.json(data)
}
catch(error){
res.status(500).json({message: error.message})
}
})
Frontend
Billing.js
import React, {Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css"
import axios from "axios"
import SimpleReactValidator from "simple-react-validator"
import TextField from '#mui/material/TextField';
import $ from 'jquery'
import Select,{components} from "react-select";
import Box from '#mui/material/Box';
import NativeSelect from "#mui/material/NativeSelect";
import Button from "#mui/material/Button";
class Billing extends Component {
constructor(){
super()
this.state = {
clientdetails :{},
paymentReferenceCode: "",
paymentType: "",
paymentDescription: "",
procedureAmount: "",
paymentDiscount: "",
AmountPaid: "",
Total: "",
}
this.changePaymentReferenceCode = this.changePaymentReferenceCode.bind(this)
this.changePaymentType = this.changePaymentType.bind(this)
this.changePaymentDescription = this.changePaymentDescription.bind(this)
this.changeProcedureAmount = this.changeProcedureAmount.bind(this)
this.changePaymentDiscount = this.changePaymentDiscount.bind(this)
this.changeAMountPaid = this.changeAMountPaid.bind(this)
this.changeTOtal = this.changeTOtal.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
changePaymentReferenceCode(event){
this.setState({
paymentReferenceCode:event.target.value
})
}
changeProcedureAmount(event){
this.setState({
procedureAmount:event.target.value
})
}
changePaymentType(event){
this.setState({
paymentType:event.target.value
})
}
changePaymentDescription(event){
this.setState({
paymentDescription:event.target.value
})
}
changePaymentAmount(event){
this.setState({
paymentAmount:event.target.value
})
}
changePaymentDiscount(event){
this.setState({
paymentDiscount:event.target.value
})
}
changeAMountPaid(event){
this.setState({
AmountPaid:event.target.value
})
}
changeTOtal(event){
this.setState({
Total:event.target.value
})
}
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
})
}
componentDidMount(){
this.loadData();
}
loadData = () => {
axios.get('http://localhost:4000/clients', {
headers: {"Access-Control-Allow-Origin": true,
'Access-Control-Allow-Credentials' : true,
'Access-Control-Allow-Methods':'GET,PUT,POST,DELETE,PATCH,OPTIONS',
crossorigin : true,
},
responseType : 'json'
})
.then((result) => {
console.log(result.data);
this.setState({
clients : result.data,
})
})
.catch((error) => {
console.log(error);
})
}
customFilter = (option, searchText) => {
if(
option.data.fullName.toLowerCase().includes(searchText.toLowerCase())
)
return true;
return false;
}
handleSubmit(event){
event.preventDefault()
const entry = {
paymentReferenceCode: this.state.paymentReferenceCode,
paymentType: this.state.paymentType,
paymentDescription: this.state.paymentDescription,
procedureAmount: this.state.procedureAmount,
paymentDiscount: this.state.paymentDiscount,
AmountPaid: this.state.AmountPaid,
Total: this.state.Total,
}
axios.post('http://localhost:4000/Bill/addbill', entry)
.then(response => {
this.setState({clientdetails: response.data})
console.log(response.data)
})
this.setState({
clientdetails:{},paymentReferenceCode: "",paymentType: "",paymentDescription: "",procedureAmount: "",
paymentDiscount: "",AmountPaid: "",Total: "",
})}
render() {
const displayNone = { display: 'none' }
return (
<div>
<div className="container">
<div className="form-div">
<p className="text-capitalize">Billing</p>
<Box component="form" onSubmit={this.handleSubmit} noValidate sx={{ mt: 1}}>
<Select
closeMenuOnSelect={true}
hideSelectedOptions={true}
options={this.state.clientdetails}
filterOption = {this.customFilter}
isClearable={true}
search={true}
components={{IndicatorSeparator: () => null,}}
placeholder={'Select Client'}
getOptionLabel={option => `${option.fullName} ${option._id}`}
onchange={this.customFilter}
></Select>
<TextField
margin="normal"
fullWidth
id="paymentReferenceCode"
label="PaymentRefernceCode"
name="paymentReferenceCode"
autoComplete="off"
value={this.state.paymentReferenceCode}
onChange={this.handleChange}
autoFocus
/>
<NativeSelect
fullWidth
onChange={this.handleChange}
value={this.state.paymentType}
inputProps={{
name: 'paymentType',
id: 'paymentType',
}}
>
<option >PaymentType</option>
<option value="Cash">Cash</option>
<option value="PayPal">PayPal</option>
<option value="MasterCard">MasterCard</option>
</NativeSelect>
<TextField
margin="normal"
fullWidth
InputLabelProps={{style : {color : 'black'} }}
id="paymentDescription"
label="Payment Description"
name="paymentDescription"
autoComplete="paymentDescription"
onChange={this.handleChange}
value={this.state.paymentDescription}
autoFocus
/>
<TextField
margin="normal"
fullWidth
id="AmountPaid"
label="Amount Paid"
name="AmountPaid"
autoComplete="AmountPaid"
onChange={this.handleChange}
value={this.state.AmountPaid}
autoFocus
/><TextField
margin="normal"
fullWidth
id="paymentDiscount"
label="Payment Discount"
name="paymentDiscount"
autoComplete="paymentDiscount"
onChange={this.handleChange}
value={this.state.paymentDiscount}
autoFocus
/>
<TextField
margin="normal"
fullWidth
id="procedureAmount"
label="Procedure Amount"
name="procedureAmount"
autoComplete="procedureAmount"
onChange={this.handleChange}
value={this.state.procedureAmount}
autoFocus
/>
<TextField
margin="normal"
fullWidth
id="Total"
label="Total Bill"
name="Total"
autoComplete="Total"
onChange={this.handleChange}
value={this.state.Total}
autoFocus
/>
<div id='loginSuccess' className="alert alert-success" style={displayNone} role="alert">
<strong>Success! </strong>Client Bill Entered Successful.
</div>
<Button
type="submit"
fullWidth
sx={{ mt: 3, mb: 2}}
>
<span className='btn btn-warning btn-block form-control form-group'>Submit</span>
</Button>
</Box>
</div>
</div>
</div>
);
}
}
export default Billing;
I tried to use axios.post and submit the form. However, am not able to retrieve clientdetails data to the frontend in particular the select part of the form, it returns null. But the other entries go through to the backend. This is what am getting in the console.
data:
AmountPaid: 100
Total: 100
createdAt: "2022-11-25T22:31:57.306Z"
clientdetails: null
paymentDescription: "accomodation"
paymentDiscount: 100
paymentReferenceCode: "2345"
paymentType: "Cash"
procedureAmount: "3"
updatedAt: "2022-11-25T22:31:57.306Z"
__v: 0
_id: "6381425db019f3f9a48047ae"
[[Prototype]]: Objectid: "6381425db019f3f9a48047ae"
I would like to retrieve the clientdetails data to the frontend select section, select an option and be able to submit all the data.Thank you
You have a few problems, both on front-end and back-end.
Try to change your component like this:
class Billing extends Component {
constructor() {
super();
this.state = {
clients: [],
clientdetails: '',
paymentReferenceCode: '',
paymentType: '',
paymentDescription: '',
procedureAmount: '',
paymentDiscount: '',
AmountPaid: '',
Total: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleChangeClientDetails = this.handleChangeClientDetails.bind(this);
}
componentDidMount() {
this.loadData();
}
handleChange(event) {
this.setState({
...this.state,
[event.target.name]: event.target.value,
});
}
handleChangeClientDetails(newValue) {
this.setState({
...this.state,
clientdetails: newValue._id
})
}
loadData = () => {
axios
.get('http://localhost:4000/clients', {
headers: {
'Access-Control-Allow-Origin': true,
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE,PATCH,OPTIONS',
crossorigin: true,
},
responseType: 'json',
})
.then((result) => {
console.log(result.data);
this.setState({
...this.state,
clients: result.data,
});
})
.catch((error) => {
console.log(error);
});
};
customFilter = (option, searchText) => {
if (option.data.fullName.toLowerCase().includes(searchText.toLowerCase()))
return true;
return false;
};
handleSubmit(event) {
event.preventDefault();
const entry = {
paymentReferenceCode: this.state.paymentReferenceCode,
paymentType: this.state.paymentType,
paymentDescription: this.state.paymentDescription,
procedureAmount: this.state.procedureAmount,
paymentDiscount: this.state.paymentDiscount,
AmountPaid: this.state.AmountPaid,
Total: this.state.Total,
};
if (!this.state.clientdetails) {
console.log('Select client');
return;
}
axios.post(`http://localhost:4000/Bill/addbill/${this.state.clientdetails}`, entry).then((response) => {
console.log(response.data);
});
this.setState({
clientdetails: '',
paymentReferenceCode: '',
paymentType: '',
paymentDescription: '',
procedureAmount: '',
paymentDiscount: '',
AmountPaid: '',
Total: '',
});
}
render() {
const displayNone = { display: 'none' };
return (
<div>
<div className='container'>
<div className='form-div'>
<p className='text-capitalize'>Billing</p>
<Box
component='form'
onSubmit={this.handleSubmit}
noValidate
sx={{ mt: 1 }}
>
<Select
closeMenuOnSelect={true}
hideSelectedOptions={true}
options={this.state.clients}
filterOption={this.customFilter}
isClearable={true}
search={true}
components={{ IndicatorSeparator: () => null }}
placeholder={'Select Client'}
getOptionLabel={(option) => `${option.fullName} ${option._id}`}
onChange={this.handleChangeClientDetails} />
...
</div>
</div>
</div>
);
}
}
export default Billing;
And add the parameter to the POST route:
router.post("/addbill/:id", ... );
At your router you're using req.params.id but it's never declared, since your clientdetails uses the clientid, it'll be null. Easy to fix.
router.post("/addbill",async(req,res)=>{
try {
console.log(req.params);
const clientid = await clients.findOne({ _id: req.params.id });
should be
// /-- req.params.id
router.post("/addbill/:id",async(req,res)=>{
try {
console.log(req.params);
const clientid = await clients.findOne({ _id: req.params.id });
just remember that now your route will need the /clientid at the end.

React Data Grid: Custom DropDown Editor: value is not getting updated. Grid is not getting enabled for editing

on react-data-grid 7.0.0-beta
I read through the most recent demos provided in git repo for react-data-grid and implemented a custom dropdown for my use case.
Dropdown seems to be working but it is not updating the grid data upon selection. The editable property doesn't seem to be working either.
test code is implemented here:
Sandbox: https://codesandbox.io/s/react-data-grid-custom-dropdown-editor-kcy5n
export const EntryCriteriaGrid = () => {
const columns = [
{
key: "r1",
name: "Criteria",
width: "50%",
resizable: true,
editable: true
},
{
key: "status",
name: "Status",
editor: DropdownCustomEditor,
editorOptions: {
editOnClick: true
},
editable: true
},
{ key: "tracker", name: "Tracker", editable: true }
];
const rows = [
{ r1: "data 1", status: "BLOCKED", tracker: "tracker 1" },
{ r1: "data 2", status: "PASS", tracker: "tracker 1" },
{ r1: "data 3", status: "ISSUE", tracker: "tracker 2" }
];
const [state, setState] = useState({ rows });
const onGridRowsUpdated = ({ fromRow, toRow, updated }) => {
setState((state) => {
const rows = state.rows.slice();
for (let i = fromRow; i <= toRow; i++) {
rows[i] = { ...rows[i], ...updated };
}
return { rows };
});
};
return (
<div>
<ReactDataGrid
columns={columns}
rows={state.rows}
rowsCount={3}
onGridRowsUpdated={onGridRowsUpdated}
enableCellSelect={true}
className="rdg-light"
/>
</div>
);
};
export default EntryCriteriaGrid;
import React, { Component } from "react";
import ReactDOM from "react-dom";
export default class DropdownCustomEditor extends Component {
constructor(props) {
super(props);
this.state = {
selected: ""
};
this.options = [
{ id: "blocked", value: "BLOCKED" },
{ id: "pass", value: "PASS" },
{ id: "issue", value: "ISSUE" },
{ id: "notStarted", value: "NOT STARTED" }
];
}
componentDidMount() {
if (this.props.row && this.props.row.status)
this.setState({ selected: this.props.row.status });
}
getValue = function () {
return { status: this.state.selected };
};
getInputNode() {
return ReactDOM.findDOMNode(this).getElementsByTagName("select")[0];
}
update(e) {
this.setState({ selected: e.target.value });
this.props.onRowChange({ ...this.props.row, status: e.target.value }, true);
}
render() {
return (
<select
className="rdg-select-editor"
onChange={(e) => this.update(e)}
autoFocus
value={this.state.selected}
>
{this.options.map((elem) => {
return (
<option key={elem.id} value={elem.value}>
{elem.value}
</option>
);
})}
</select>
);
}
}
Just change your code as follows:
In DropdownCustomEditor component:
update(e) {
this.setState({ selected: e.target.value });
this.props.onRowChange({ ...this.props.row, status: e.target.value });
}
In EntryCriteriaGrid component
const onGridRowsUpdated = (rows) => {
setState({ rows });
};
and
<ReactDataGrid
columns={columns}
rows={state.rows}
rowsCount={3}
//onRowsUpdate={onGridRowsUpdated}
enableCellSelect={true}
className="rdg-light"
onRowsChange={(rows) => onGridRowsUpdated(rows)}
/>

TypeError this.getData is not a function

index.js
export default class App extends React.Component {
state = {
data: [],
filteredData: [],
columns: [],
searchInput: ""
};
componentDidMount() {
this.getData();
this.getColumns();
}
render() {
const getColumns = () => {
let columns = [
{
Header: "First Name",
accessor: "firstName",
sortable: false,
show: true,
displayValue: " First Name"
},
{
Header: "Status",
accessor: "status",
sortable: false,
show: true,
displayValue: "Status "
},
{
Header: "Visits",
accessor: "visits",
sortable: false,
show: true,
displayValue: " Visits "
}
];
this.setState({ columns });
};
const getData = () => {
let data = [
{ firstName: "test1", status: "hi1", visits: 155 },
{ firstName: "test2", status: "hi2", visits: 155 },
];
this.setState({ data, filteredData: data });
};
const handleSetData = (data) => {
console.log(data);
this.setState({ filteredData: data });
};
let { filteredData, columns } = this.state;
return (
<div>
<test
data={this.state.data}
handleSetData={this.handleSetData}
/>
<ReactTable
data={filteredData}
columns={columns}
defaultPageSize={10}
className="-striped -highlight"
/>
</div>
);
}
}
error code
TypeError
this.getData is not a function
App.componentDidMount
/src/index.js:17:11
14 | };
15 |
16 | componentDidMount() {
> 17 | this.getData();
| ^
18 | this.getColumns();
19 | }
20 |
I am currently practicing because I want to implement the search function.
I want it to work without changing the current code location.
I will post more code if needed.
How to enable getData and get columns from componentDidMount?
Let me know if you know the answer! thank you for the reply.
You can't setState inside render. You need to write function outside render that's why its giving error. As it can't find out the functions. You need to do like this:
export default class App extends React.Component {
state = {
data: [],
filteredData: [],
columns: [],
searchInput: ""
};
componentDidMount() {
this.getData();
this.getColumns();
}
getColumns = () => {
let columns = [
{
Header: "First Name",
accessor: "firstName",
sortable: false,
show: true,
displayValue: " First Name"
},
{
Header: "Status",
accessor: "status",
sortable: false,
show: true,
displayValue: "Status "
},
{
Header: "Visits",
accessor: "visits",
sortable: false,
show: true,
displayValue: " Visits "
}
];
this.setState({ columns });
};
getData = () => {
let data = [
{ firstName: "test1", status: "hi1", visits: 155 },
{ firstName: "test2", status: "hi2", visits: 155 },
];
this.setState({ data, filteredData: data });
};
handleSetData = (data) => {
console.log(data);
this.setState({ filteredData: data });
};
render() {
let { filteredData, columns } = this.state;
return (
<div>
<test
data={this.state.data}
handleSetData={this.handleSetData}
/>
<ReactTable
data={filteredData}
columns={columns}
defaultPageSize={10}
className="-striped -highlight"
/>
</div>
);
}
}

Autosuggest/Combobox suggestion wont display

For some reason the suggestion wont display, there are no error messages in console.
Senario
User clicks on combobox, suggestion are displayed. (Working)
User selects a suggestion. Suggestion is displayed in combobox.
(Selection its loading in a single span)
If user clicks on combobox after making a selection, combobox will clear value, but previous selection will display as a highlighted first choice. Also updated suggestion display below first choice.
( I really need help on this one, I can see the data in the console log. But since the suggestions are not displaying I can tell if its displays as requested )
(see image for reference)
react-autosuggest react-autosuggest#9.3.4
Component Code
import React, { Component } from "react";
import ReactDOM, { findDOMNode } from "react-dom";
import PropTypes from "prop-types";
import classNames from "classnames";
import Autosuggest from "react-autosuggest";
import Icon from '../Components/Icons/Icons';
class Autocomplete extends Component {
constructor() {
super();
this.state = {
value: "",
suggestions: [],
isTouched: false,
multi: false,
selectedInput: ""
};
this.onChange = this.onChange.bind(this);
this.onClick = this.onClick.bind(this);
this.blurCallback = this.blurCallback.bind(this);
this.triggerFocus = this.triggerFocus.bind(this);
this.handleClear = this.handleClear.bind(this);
}
getSuggestionValue = suggestion => suggestion.text;
renderSuggestion = suggestion => (<span>{suggestion.text}</span>)
escapeRegexCharacters = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
getSectionSuggestions = section => section && section.items;
getSuggestions = (value, selected, reason) => {
let suggestions = this.props.data;
if (value !== undefined) {
const escapedValue = this.escapeRegexCharacters(value.trim());
const selectedInput = [];
selectedInput.push(selected);
const regex = new RegExp(escapedValue, "i");
const filtered = suggestions.filter(language =>
regex.test(language.text)
);
if (escapedValue === "") {
return {
suggestions: [
{ text: selectedInput[0], items: [] },
{ text: "", items: filtered }
],
multi: true
};
}
if (!filtered.length) {
return {
suggestions: selectedInput.length > 0 ? selectedInput : filtered,
multi: false
};
}
return {
suggestions: [{
text: selectedInput[0],
items: selectedInput.length > 0 ? selectedInput : filtered
},
{
text: "",
items: reason === "focused" ? suggestions : filtered
}],
multi: true
};
} else return;
};
onSuggestionsFetchRequested = ({ value, reason }) => {
if (reason === "input-focused") {
this.setState({ value: "" });
const { multi, suggestions } = this.getSuggestions(
value,
this.state.selectedInput ? this.state.selectedInput : "",
"focused"
);
this.setState({
suggestions,
multi
});
} else {
const { multi, suggestions } = this.getSuggestions(
value,
this.state.selectedInput ? this.state.selectedInput : "",
"notfocused"
);
this.setState({
suggestions,
multi
});
}
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
multi: false
});
};
onChange = (event, { newValue, method }) => {
if (method === "enter") {
this.setState({
value: this.state.value
});
} else {
this.setState({
value: newValue
});
}
if(this.props.search) {
this.props.search(newValue, ReactDOM.findDOMNode(this).parentNode.parentNode.querySelectorAll('li'));
};
};
onSuggestionSelected = (ev,
{ suggestion, suggestionValue, suggestionIndex, sectionIndex, method }
) => {
this.setState({
selectedInput: suggestionValue
});
};
blurCallback (ev) {
this.setState({ isTouched: false });
}
handleClear() {
this.setState({
value: ''
})
}
onClick(ev) {
this.setState({ isTouched: true });
}
triggerFocus() {
const input = document.getElementById(this.props.id);
input.focus();
}
render() {
const theme = {
container: "el-form",
containerOpen: "react-autosuggest__container--open",
input: "autocomplete form-control",
inputOpen: "react-autosuggest__input--open",
inputFocused: "react-autosuggest__input--focused",
suggestionsContainer: "react-autosuggest__suggestions-container",
suggestionsContainerOpen:
"react-autosuggest__suggestions-container--open",
suggestionsList: "autocomplete-wrap",
suggestion: "react-autosuggest__suggestion",
suggestionFirst: "react-autosuggest__suggestion--first",
suggestionHighlighted: "react-autosuggest__suggestion--highlighted",
sectionContainer: "react-autosuggest__section-container",
sectionContainerFirst: "react-autosuggest__section-container--first",
sectionTitle: "react-autosuggest__section-title"
};
const {
className,
placeholder,
data,
disabled,
label,
labelClass,
icon,
iconSize,
iconClass,
clear,
clearClass,
id,
search,
...attributes
} = this.props;
const labelClassFix = classNames(
isNotEmpty && "active",
disabled && "disabled",
labelClass
);
const iconClassFix = classNames(
"prefix",
this.state.isTouched && "active",
iconClass
);
const clearClassFix = classNames(
clearClass
)
const isclearVisible = () => {
let hiddenOrNot = "hidden"
if (this.state.value) {
hiddenOrNot = "visible";
}
return hiddenOrNot;
}
const clearStyleFix = {
position: "absolute",
zIndex: 2,
top: "2.5rem",
right: "10px",
border: "none",
background: "0 0",
visibility: isclearVisible(),
}
let isNotEmpty =
Boolean(this.state.value) || placeholder || this.state.isTouched;
const { value, suggestions, multi } = this.state;
const inputProps = {
placeholder: placeholder,
value,
onChange: this.onChange,
onBlur: this.blurCallback,
onClick: this.onClick,
onFocus: this.onFocus,
id: this.props.id,
name: this.props.name
};
const renderInputComponent = inputProps => (
<div>
{ icon && <Icon icon={icon} className={iconClassFix}/> }
<input
type="text"
id={id}
name={name}
className="form-control"
{...inputProps}
onFocus={(ev, val) => {
this.onClick();
inputProps.onFocus(ev, val);
}}
/>
<label
htmlFor={id}
id={`label for ${id}`}
onClick={this.triggerFocus}
className={labelClassFix}
>
{ label }
</label>
{ clear &&
<Icon icon="close" onClick={this.handleClear} style={clearStyleFix}
className={clearClassFix}/>
}
</div>
);
return (
<Autosuggest
multiSection={multi}
renderSectionTitle={this.renderSuggestion}
getSectionSuggestions={this.getSectionSuggestions}
suggestions={suggestions}
highlightFirstSuggestion={true}
focusInputOnSuggestionClick={false}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
getSuggestionValue={this.getSuggestionValue}
renderSuggestion={this.renderSuggestion}
inputProps={inputProps}
theme={theme}
renderInputComponent={renderInputComponent}
shouldRenderSuggestions={ () => true }
onSuggestionSelected={this.onSuggestionSelected}
/>
);
}
}
Autocomplete.propTypes = {
className: PropTypes.string,
icon: PropTypes.string,
id: PropTypes.string,
name: PropTypes.string,
};
Autocomplete.defaultProps = {
id: 'autocomplete-1',
name: '',
clear: true
};
export default Autocomplete;
In use Component
const states = [ "Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illnois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming" ];
<Autocomplete
data={states}
label="Choose your state"
icon="lightbulb"
clear
clearClass="grey-text" id="combobox-states" name="state-selection"
className="mx-auto"
/>
Codesandbox
you need a return in some of your callbacks :
getSuggestionValue = suggestion => {
return suggestion;
}
getSectionSuggestions = (section) => {
return section && section.items;
}
Edit
In getSuggestions you need to return an array directly, not an object.
see https://codesandbox.io/s/w04jpzjr4k

Antd UI Table: Dynamically add/delete columns

Based on ANTD's Table example: https://ant.design/components/table/#components-table-demo-edit-cell, I would like to replicate this, with the addition of having the ability to add/delete new columns. The sample from the link above only illustrates how to add new rows.
Here's the code from the sample:
import { Table, Input, Button, Popconfirm, Form } from 'antd';
const FormItem = Form.Item;
const EditableContext = React.createContext();
const EditableRow = ({ form, index, ...props }) => (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
);
const EditableFormRow = Form.create()(EditableRow);
class EditableCell extends React.Component {
state = {
editing: false,
}
componentDidMount() {
if (this.props.editable) {
document.addEventListener('click', this.handleClickOutside, true);
}
}
componentWillUnmount() {
if (this.props.editable) {
document.removeEventListener('click', this.handleClickOutside, true);
}
}
toggleEdit = () => {
const editing = !this.state.editing;
this.setState({ editing }, () => {
if (editing) {
this.input.focus();
}
});
}
handleClickOutside = (e) => {
const { editing } = this.state;
if (editing && this.cell !== e.target && !this.cell.contains(e.target)) {
this.save();
}
}
save = () => {
const { record, handleSave } = this.props;
this.form.validateFields((error, values) => {
if (error) {
return;
}
this.toggleEdit();
handleSave({ ...record, ...values });
});
}
render() {
const { editing } = this.state;
const {
editable,
dataIndex,
title,
record,
index,
handleSave,
...restProps
} = this.props;
return (
<td ref={node => (this.cell = node)} {...restProps}>
{editable ? (
<EditableContext.Consumer>
{(form) => {
this.form = form;
return (
editing ? (
<FormItem style={{ margin: 0 }}>
{form.getFieldDecorator(dataIndex, {
rules: [{
required: true,
message: `${title} is required.`,
}],
initialValue: record[dataIndex],
})(
<Input
ref={node => (this.input = node)}
onPressEnter={this.save}
/>
)}
</FormItem>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }}
onClick={this.toggleEdit}
>
{restProps.children}
</div>
)
);
}}
</EditableContext.Consumer>
) : restProps.children}
</td>
);
}
}
class EditableTable extends React.Component {
constructor(props) {
super(props);
this.columns = [{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
}, {
title: 'age',
dataIndex: 'age',
}, {
title: 'address',
dataIndex: 'address',
}, {
title: 'operation',
dataIndex: 'operation',
render: (text, record) => (
this.state.dataSource.length >= 1
? (
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
Delete
</Popconfirm>
) : null
),
}];
this.state = {
dataSource: [{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
}, {
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
}],
count: 2,
};
}
handleDelete = (key) => {
const dataSource = [...this.state.dataSource];
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
}
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
name: `Edward King ${count}`,
age: 32,
address: `London, Park Lane no. ${count}`,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
});
}
handleSave = (row) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
this.setState({ dataSource: newData });
}
render() {
const { dataSource } = this.state;
const components = {
body: {
row: EditableFormRow,
cell: EditableCell,
},
};
const columns = this.columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: this.handleSave,
}),
};
});
return (
<div>
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
Add a row
</Button>
<Table
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={columns}
/>
</div>
);
}
}
ReactDOM.render(<EditableTable />, mountNode);
You can make your columns array a part of the state and update it through setState.
Here is a working codepen: https://codepen.io/gges5110/pen/GLPjYr?editors=0010
// State
this.state = {
dataSource: [{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
}, {
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
}],
columns: [{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
}, {
title: 'age',
dataIndex: 'age',
}]
};
// Event to add new column
handleAddColumn = () => {
const { columns } = this.state;
const newColumn = {
title: 'age',
dataIndex: 'age',
};
this.setState({
columns: [...columns, newColumn]
});
}
// Render method
render() {
const { dataSource, columns } = this.state;
return (
<Table
dataSource={dataSource}
columns={columns}
/>
);
}

Resources