I've made a table with one column as checkbox to select that row. So if the user checks this row's checkbox, I'll add isChecked : true property in state and on uncheck will change isChecked: false of that in state. Each page is having 10rows. The issue is when I checked the 1st row checkbox of 1st page and when I go to Next Page somehow the 1st row of next page checkbox also appears checked. However only 1st row is set to true in state. Whats the issue? What wrong I'm doing can anyone tell? Thanks in advance!
import React,{Component} from 'react';
import { Table,Button,Input } from 'reactstrap';
import SelectedUsers from './SelectedUsers';
import { yellow } from '#material-ui/core/colors';
import Icon from '#material-ui/core/Icon';
class Users extends Component {
constructor(props) {
super(props);
this.state = {
users : [],
pageSize: 10,
pageIndex: 0,
selectedUsers : [],
filterCandidate : '',
searchVal : ""
};
}
componentDidMount() {
const userLink = 'api';
fetch(userLink, {
method: 'GET'
})
.then(res => res.json())
.then(data => {
this.setState({
users : data
})
console.log(data)
})
}
onSelectUser = (e,i) => {
const copy_users = this.state.users.slice() ;
const checked = e.target.checked
copy_users[i].isChecked = checked
this.setState({ copy_users})
// console.log( e.target.value)
}
handlePrevPageClick = (event) => {
this.setState(prevState => ({
pageIndex: prevState.pageIndex > 0 ? prevState.pageIndex - 1 : 0
}));
}
handleNextPageClick = (event) => {
this.setState(prevState => ({
pageIndex:
prevState.pageIndex <
Math.floor(prevState.users.length / prevState.pageSize)
? prevState.pageIndex + 1
: prevState.pageIndex
}));
}
render() {
let profile = 'Profile Image';
return (
<div className="bets_page">
<Table striped responsive>
<thead>
<tr>
<th>Select</th>
<th>Player Name</th>
<th>Level<Icon style={{ color: yellow[800] }} fontSize="small">star</Icon></th>
<th>Avatar</th>
<th>BET</th>
<th>Wins<Icon style={{ color: yellow[800] }} fontSize="small">euro</Icon></th>
<th>Lost</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{this.state.users.slice(
this.state.pageIndex * this.state.pageSize,
this.state.pageIndex * this.state.pageSize + this.state.pageSize
).map((data,i) => (
<tr key={i}>
<td>
<label className="checkbox">
<input type="checkbox"
checked={data.isChecked}
key={i}
value={data.Name}
onChange={(e) => this.onSelectUser(e,i)}/>
</label>
</td>
<td>{data.Name}</td>
<td></td>
<td><img src={data[profile]} alt={data.Name}
className="avatar"/></td>
<td>{data.Bet}</td>
<td></td>
<td></td>
<td>{data.Price}</td>
</tr>
))}
</tbody>
</Table>
<div>
<Button onClick={event => this.handlePrevPageClick(event)} className="m-2">
{"<"}
</Button>Page {this.state.pageIndex+1}
<Button onClick={event => this.handleNextPageClick(event)} className="m-2">
{">"}
</Button>
</div>
</div>
}
}
export default Users;
when you slice the users and apply map on them the 'i' variable starts from 0 for each page. you should add 'this.state.pageIndex * this.state.pageSize' to 'i' variable whenever you set it for key and you send that to onSelectUser
render() {
return (
<div className="bets_page">
<Table striped responsive>
<thead>
<tr>
<th>Select</th>
<th>Player Name</th>
</tr>
</thead>
<tbody>
{this.state.users
.slice(
this.state.pageIndex * this.state.pageSize,
this.state.pageIndex * this.state.pageSize + this.state.pageSize
)
.map((data, i) => {
const index = i + this.state.pageIndex * this.state.pageSize;
return (
<tr key={index}>
<td>
<label className="checkbox">
<input
type="checkbox"
checked={data.isChecked}
key={i}
value={data.Name}
onChange={(e) => this.onSelectUser(e, index)}
/>
</label>
</td>
<td>{data.Name}</td>
</tr>
);
})}
</tbody>
</Table>
<div>
<Button
onClick={(event) => this.handlePrevPageClick(event)}
className="m-2"
>
{"<"}
</Button>
Page {this.state.pageIndex + 1}
<Button
onClick={(event) => this.handleNextPageClick(event)}
className="m-2"
>
{">"}
</Button>
</div>
</div>
);
}
I simplified your code and I created the online demo here
Related
How can i get sum of column values for each row and push each row's total in rowTotalArray.
Image is Here
The number of rows and input fields are dynamic. When user enters number in input field for each column in row , last column (Total) must show sum of column value for each row.
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import Table from 'react-bootstrap/Table';
function RowWiseSum() {
const datas = useSelector(state => state.dataList.datas);
const [group, setGroup] = useState([{
g1: 0,
g2: 0,
g3: 0,
}]);
const [sum, setSum] = useState(0);
const [rowTotalArray, setRowTotalArray] = useState([]);
useEffect(() => {
let sum = parseFloat(group.g1) + parseFloat(group.g2) + parseFloat(group.g3);
setSum(sum);
}, [group])
return (
<Table striped bordered size="sm" style={{ fontSize: '12px' }}>
<thead>
<tr>
<th>Sn</th>
<th>Title</th>
<th>No. in G1</th>
<th>No. in G2</th>
<th>No. in G3</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{
datas.map((data, i) =>
(
<tr key={i}>
<td>{data.sno}</td>
<td>{data.description}</td>
<td>
<input type='text' name={`g1-${i+1}`} onChange={(e) => setGroup({ ...group, g1: e.target.value })} />
</td>
<td>
<input type='text' name={`g2-${i+1}`} onChange={(e) => setGroup({ ...group, g2: e.target.value })} />
</td>
<td>
<input type='text' name={`g3-${i+1}`} onChange={(e) => setGroup({ ...group, g3: e.target.value })} />
</td>
<td>
<input type='text' readOnly value={sum} name={`sum-${i+1}`} />
</td>
</tr>
))
}
</tbody>
</Table>
)
}
export default RowWiseSum
Your group state should be array of each row point, so you can caculate total of each row
const defaultGroup = datas.map((i) => ({
g1: 0,
g2: 0,
g3: 0
}));
const [group, setGroup] = useState(defaultGroup);
const onChange = (type, value, index) => {
const newGroup = group.map((g, idx) => {
if (index === idx)
return {
...g,
[type]: parseInt(value || 0)
};
return g;
});
setGroup(newGroup);
};
const total = group.reduce((acc, cur) => {
acc += cur.g1 + cur.g2 + cur.g3;
return acc;
}, 0);
return (
<div className="App">
<table striped bordered size="sm" style={{ fontSize: "12px" }}>
<thead>
<tr>
<th>Sn</th>
<th>Title</th>
<th>No. in G1</th>
<th>No. in G2</th>
<th>No. in G3</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{datas.map((data, i) => (
<tr key={i}>
<td>{data.sno}</td>
<td>{data.description}</td>
<td>
<input
type="number"
name={`g1-${i + 1}`}
defaultValue={group[i].g1}
onChange={(e) => onChange("g1", e.target.value, i)}
/>
</td>
<td>
<input
type="number"
name={`g2-${i + 1}`}
defaultValue={group[i].g2}
onChange={(e) => onChange("g2", e.target.value, i)}
/>
</td>
<td>
<input
type="number"
name={`g3-${i + 1}`}
defaultValue={group[i].g3}
onChange={(e) => onChange("g3", e.target.value, i)}
/>
</td>
<td>
<input
type="text"
readOnly
name={`sum-${i + 1}`}
value={group[i].g1 + group[i].g2 + group[i].g3}
/>
</td>
</tr>
))}
</tbody>
</table>
<div>TOTAL: {total}</div>
</div>
);
I've created a codesandbox, you can check it. Hope it help!
your group state should be like this
const [group, setGroup] = useState({ g1: 0, g2: 0, g3: 0, });
instead of
const [group, setGroup] = useState([{g1: 0, g2: 0, g3: 0,}])
AS a Learner in ReactJS ,I have tried to perform Crud operation on the dynamic form But failed to perform a Update operation i.e edit button .i want to pass specific row data to the ADDform component from the Appcomponent Table and edit the data in addForm Component and save the data.I am unable to show data in respected addform component inputs when click on edit button?
How to perform Edit operation ?
import React, { useState } from "react";
import Select from "react-select";
const AddForm = (props) => {
const newdata = props.editdata;
const [id, setId] = React.useState(newdata["id"]?newdata.id:"");
const [name, setName] = React.useState(newdata["name"]?newdata.name:"");
const [gender, setGender] = React.useState(()=>{
let _gender= newdata["gender"]?.toLowerCase() || ""
let id_ = props.List.filter( x => {return x["label"]?.toLowerCase() ===_gender } ) ||""
console.log(id_)
return id_[0]["label"]
});
console.log(gender)
return (
<tr>
<td>
<input
type="text"
value={id}
onChange={(e) => {
setId(e.target.value);
}}
></input>
</td>
<td>
<input
type="text"
value={name}
onChange={(e) => {
setName(e.target.value);
}}
></input>
</td>
<td>
<Select options={props.List} value={gender} onChange={setGender} />
</td>
<td>
<button
onClick={(e) => {
props.addRow({
id: id,
name: name,
genderId: gender.value,
gender: gender.label
});
setId("");
setName("");
setGender("");
}}
>
Add
</button>
</td>
</tr>
);
};
const List = [
{
value: 1,
label: "Male"
},
{ value: 2, label: "Female" }
];
const App = (props) => {
const [data, setData] = React.useState([]);
const [edit, isEdit] = useState(false);
const [editdata, setEditData] = useState([]);
return (
<div>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Gender</th>
<th>Action</th>
</tr>
{data &&
data.map((row, idx) => {
return (
<tr key={idx}>
<td>{row.id}</td>
<td>{row.name}</td>
<td>{row.gender}</td>
<td>
<button
onClick={(e) => {
let _data = data.filter((item) => {
return item.id !== row.id;
});
setData(_data);
}}
>
Delete
</button>
</td>
<td>
<button
onClick={(e) => {
// isEdit(true);
setEditData(row);
// console.log(editdata);
<AddForm />;
}}
>
Edit
</button>
</td>
</tr>
);
})}
<AddForm
data={data}
List={List}
setData={setData}
editdata={editdata}
addRow={(e) => {
setData([...data, e]);
}}
/>
</table>
</div>
);
};
export default App;
There is a 'Print' button on SaleInvoice.js which shows the Modal containing Report.js.
Report.js is created only to describe how and what should be displayed in the printed page.
Report.js has a button 'Print' which opens the Print Preview in the browser.
How do I make the 'Print' button on SaleInvoice.js directly open the Print Preview in the browser?
SaleInvoice.js
...
<button
style={{marginRight: '200px'}}
onClick={() => this.setState({printView: true})}>
Print
</button>
{this.state.printView ? (
<Modal
type="Print"
{...this.props}
show={this.state.printView}
modalClosed={() => this.setState({printView: false})}
/>
) : null}
...
Report.js
import React from 'react';
import styles from './SaleInvoice.module.css';
const SaleInvoice = props => {
const {customerName} = props;
const rows = props.lineItems;
const alignRightMarginRight = [styles.alignRight, styles.marginRight].join(
' ',
);
const {amountBeforeFreight, freight, amountAfterFreight} = props;
const reverseDateString = date => {
let [yyyy, mm, dd] = date.split('-');
return dd + '-' + mm + '-' + yyyy;
};
const renderContent = () => {
let i = 0;
let contentRows = (i, d) => {
return (
<React.Fragment>
<td className={styles.serialNumber}>{i}</td>
<td className={styles.productName}>{d.product_name}</td>
<td className={alignRightMarginRight}>{d.product_qty}</td>
<td className={alignRightMarginRight}>{d.product_unit}</td>
<td className={alignRightMarginRight}>{d.product_rate}</td>
<td className={alignRightMarginRight}>{d.product_disc}</td>
<td className={alignRightMarginRight}>{d.sub_total}</td>
</React.Fragment>
);
};
return rows.map(d => {
i++;
if (i === rows.length) {
return null;
}
if (i % 10 === 0) {
return (
<tr key={i} className={styles.pagebreak}>
{contentRows(i, d)}
</tr>
);
} else {
return <tr key={i}>{contentRows(i, d)}</tr>;
}
});
};
return (
<React.Fragment>
<div className={styles.all}>
<div className={[styles.header].join(' ')}>
<button className={styles.hideInPrint} onClick={window.print}>
Print
</button>
<button className={styles.hideInPrint} onClick={props.modalClosed}>
Close
</button>
<h5 className={styles.docTitle}>Estimate</h5>
<h6 className={styles.customerName}>
{customerName}
<span className={styles.date}>
{reverseDateString(props.date.split('T')[0])}
</span>
</h6>
</div>
<table className={styles.content}>
<thead>
<tr>
<td>No</td>
<td>Name</td>
<td className={alignRightMarginRight}>Qty</td>
<td className={alignRightMarginRight}>Unit</td>
<td className={alignRightMarginRight}>Rate</td>
<td className={alignRightMarginRight}>Disc</td>
<td className={alignRightMarginRight}>Total</td>
</tr>
</thead>
<tbody className={styles.content}>{renderContent()}</tbody>
</table>
<div className={styles.footer}>
<div className={styles.foot}>
<label>Amount:</label>
<label className={styles.amount}>
{amountBeforeFreight.toFixed(2)}
</label>
</div>
<div className={styles.foot}>
<label>Freight:</label>
<label className={styles.amount}>
{freight ? freight.toFixed(2) : 0}
</label>
</div>
<div className={styles.foot}>
<label>Final Amount:</label>
<label className={styles.amount}>
{amountAfterFreight.toFixed(2)}
</label>
</div>
</div>
</div>
</React.Fragment>
);
};
export default SaleInvoice;
in your file SalesInvoice.js, we would be changing the event onClick, instead of what action does(setting modal view to true in your state) we want it to open the print window in the browser.
as follows
<button
style={{marginRight: '200px'}}
onClick={window.print} >
Print
</button>
you will not need this part I guess?
{this.state.printView ? (
<Modal
type="Print"
{...this.props}
show={this.state.printView}
modalClosed={() => this.setState({printView: false})}
/>
) : null}
The first select, when selected, should filter the second, or popular through a request to Api.
The problem I'm having is that when entering the second field, the state is shared to the options, and when modifying any field, modifies all the options.
class FieldCentroCusto extends Component {
constructor(){
super()
this.state = {
itensObra: []
}
this.toggleItens = this.toggleItens.bind(this)
}
toggleItens(event) {
this.props.optionsObras.map(obra => {
if (event.target.value === obra.nome) {
return this.setState({
itensObra: obra.itens
})
}
return false
})
}
render() {
const renderItens = ({ fields, optionsObras }) => (
<div>
<Button color="primary" type="button" onClick={() => fields.push({})}>
<i className="fa fa-plus"></i> Adicionar Item de Custo
</Button>
<br /><br />
<table className="table table-bordered table-hover">
<thead className="thead-inverse">
<tr>
<th>Obra</th>
<th>Centro de Custo</th>
</tr>
</thead>
<tbody>
{fields.map((item, itemIndex) =>
<tr key={itemIndex}>
<td>
<Field name={`${item}.obra`} component={LabelAndSelect}
onChange={this.toggleItens} options={
optionsObras.map((option, optionIndex) => {
return {value: `${option.nome === undefined?'':
option.nome}`, label: `${option.nome === undefined?'':
option.nome}`}
}
)} />
</td>
<td>
<Field name={`${item}.itemObra`} component={LabelAndSelect}
options={this.state.itensObra.map((option, optionIndex) => {
return {value: `${option.descricao === undefined?'':
option.descricao}`, label: `${option.descricao === undefined?'':
option.descricao}`}
}
)} />
</td>
</tr>
)}
</tbody>
</table>
</div>
)
return (
<FieldArray name="centrosCusto"
optionsObras={this.props.optionsObras}
component={renderItens} />
)
}
}
I got it sorted out.
I passed the FieldArray index in the onChange event of the selection and in the state where I placed an array with the passed index. So I can fill in the other selection options with specific indexes in the array.
constructor(){
super()
this.state = {
itensObra: []
}
this.toggleItens = this.toggleItens.bind(this)
}
toggleItens(event, index) {
const itens = this.state.itensObra
this.props.optionsObras.map(obra => {
if (event.target.value === obra.nome) {
itens[index] = obra.itens
return this.setState({
itensObra: itens
})
}
return false
})
}
Here are the Fields:
<td>
<Field name={`${item}.obra`} component={LabelAndSelect}
onChange={(event) => this.toggleItens(event, itemIndex)}
options={optionsObras.map((option, optionIndex) => {
return {value: `${option.nome === undefined?'':option.nome}`,
label:`${option.nome === undefined?'':option.nome}`}
}
)} />
</td>
<td>
<Field name={`${item}.itemObra`} component={LabelAndSelect}
options={this.state.itensObra[itemIndex]?
this.state.itensObra[itemIndex].map((option, optionIndex) => {
return {value: `${option.descricao === undefined?'':option.descricao}`,
label: `${option.descricao === undefined?'':option.descricao}`}
}
):[]} />
</td>
I am a professional web developer teaching myself react. I created this table as part of a larger form.
The table is invoked inside the form component
<ProductList
products={this.state.products}
onChange={products => this.sendUpdate('products', products)}
/>
this.sendUpdate:
sendUpdate(field, value) {
this.setState({[field]: value});
socket.emit('updateItem', this.state.id, {[field]: value});
}
That part is all working great with all my form updates. but now I am trying to figure out how to process the updates inside the table. Each product is a row of the table invoked like this:
<tbody>
{this.props.products.map((product, i) =>
<Product key={i} data={product} products={this}/>
)}
</tbody>
What is the proper way to update the state when I type in one of the inputs?
<FormControl
value={this.props.data.species}
onClick={e => this.updateProduct('species', e.target.value)}
/>
full code for ProductList
import React from "react";
import {Button, Table, FormControl} from "react-bootstrap";
class Product extends React.Component {
updateField(...props){
this.props.products.updateProduct(this.data, ...props)
}
render() {
return (
<tr>
<td>
<FormControl
value={this.props.data.species}
onClick={e => this.updateProduct('species', e.target.value)}
/>
</td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl type="number"/></td>
<td><Button bsStyle="danger" onClick={() => this.props.products.deleteProduct(this.props.data)}>X</Button></td>
</tr>
);
}
}
export default class ProductList extends React.Component {
constructor(...props) {
super(...props);
}
addProduct() {
let products = this.props.products.concat([{timestamp: Date.now()}]);
this.props.onChange(products);
}
updateProduct(product, field, newValue) {
this.props.products;
// ???
}
deleteProduct(product) {
let products = this.props.products.filter(p => {
return p !== product
});
this.props.onChange(products);
}
render() {
return (
<Table responsive>
<thead>
<tr>
<th>Species</th>
<th>Dried</th>
<th>Cut</th>
<th>Dimensions Green</th>
<th>Dimensions Dry</th>
<th>Color</th>
<th>Quantity</th>
<th className="text-right">
<Button bsStyle="success" bsSize="xsmall" onClick={() => this.addProduct()}>Add</Button>
</th>
</tr>
</thead>
<tbody>
{this.props.products.map(product => <Product key={product.timestamp} data={product} products={this}/>)}
</tbody>
</Table>
);
}
}
This is what I ended up with based on the accepted answer:
import React from "react";
import {Button, Table, FormControl} from "react-bootstrap";
export default class ProductList extends React.Component {
constructor(...props) {
super(...props);
}
addProduct() {
let products = this.props.products.concat([{}]);
this.props.onChange(products);
}
updateProduct(product, field, newValue) {
const products = this.props.products.map(p => {
return p === product ? {...p, [field]: newValue} : p;
});
this.props.onChange(products);
}
deleteProduct(product) {
let products = this.props.products.filter(p => {
return p !== product
});
this.props.onChange(products);
}
render() {
return (
<Table responsive striped>
<thead>
<tr>
<th>Species</th>
<th>Dried</th>
<th>Cut</th>
<th>Dimensions Green</th>
<th>Dimensions Dry</th>
<th>Color</th>
<th>Quantity</th>
<th className="text-right">
<Button bsStyle="success" bsSize="xsmall" onClick={() => this.addProduct()}>Add</Button>
</th>
</tr>
</thead>
<tbody>
{this.props.products.map((product, i) => this.renderRow(i, product, this))}
</tbody>
</Table>
);
}
renderRow(i, product) {
return (
<tr key={i}>
<td>
<FormControl
value={product.species || ''}
onChange={e => this.updateProduct(product, 'species', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.dried || ''}
onChange={e => this.updateProduct(product, 'dried', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.cut || ''}
onChange={e => this.updateProduct(product, 'cut', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.dimensionsGreen || ''}
onChange={e => this.updateProduct(product, 'dimensionsGreen', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.dimensionsDry || ''}
onChange={e => this.updateProduct(product, 'dimensionsDry', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.color || ''}
onChange={e => this.updateProduct(product, 'color', e.target.value)}
/>
</td>
<td>
<FormControl
type="number"
value={product.quantity || 0}
onChange={e => this.updateProduct(product, 'quantity', e.target.value)}
/>
</td>
<td><Button bsStyle="danger" onClick={() => this.deleteProduct(product)}>X</Button></td>
</tr>
);
}
}
In your ProductsList's render(), change the array map to something like:
{this.props.products.map((product, index) => <Product key={product.timestamp} data={product} index={index} products={this}/>)}
Then in your Product's change the updateField() to:
updateField(...props){
this.props.products.updateProduct(this.props.index, ...props)
}
And finally, change ProductsList's updateProduct() to:
updateProduct(index, field, newValue) {
const products = this.props.products.map((product, productIndex)) => {
if (index === productIndex) {
return {
...product,
[field]: newValue
};
}
return product;
})
this.props.onChange(products);
}
Also, there's a slight typo in Product render. The FormControl's onClick should read onClick={e => this.updateField('species', e.target.value)}.