Open Modal from Table Row in React - reactjs

openBookDetails sets isBookDetailsOpen: true which should change the className in showHideClassName to modal display-block and it should show on the screen but the modal doesn't show. However it appears in Elements in the Development tools
import React, { Component } from 'react';
import './Update.css';
import Search from '../Search/Search';
// import Modal from './Modal/Modal';
const Table = ({ data, openBookDetails }) => (
<table class="table table-hover">
<thead>
<tr class="table-primary">
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">ISBN</th>
<th scope="col">No. Of Copies</th>
</tr>
</thead>
<tbody>
{data.map(row =>
<TableRow key={row.id} row={row} openBookDetails={openBookDetails}/>
)}
</tbody>
</table>
)
const TableRow = ({ row, openBookDetails }) => (
<tr class="table-light" onClick={openBookDetails}>
<th scope="row" >{row.title}</th>
<td >{row.author}</td>
<td >{row.isbn}</td>
<td >24</td>
</tr>
)
const Modal = ({ closeBookDetails, isBookDetailsOpen, children }) => {
const showHideClassName = isBookDetailsOpen ? 'modal display-block' : 'modal display-none';
return (
<div className={showHideClassName}>
<section className='modal-main'>
{children}
<button
onClick={closeBookDetails}
>
Close
</button>
</section>
</div>
);
};
class Update extends Component{
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
setOfAllBooks: [],
searchedBooks: [],
isBookDetailsOpen: false,
};
this.openBookDetails = this.openBookDetails.bind(this);
this.setTableData = this.setTableData.bind(this);
}
setTableData(searchedBook){
this.setState({searchedBooks: searchedBook})
console.log(this.state.searchedBooks)
}
openBookDetails(){
console.log('openBookDetails')
this.setState({ isBookDetailsOpen: true})
}
closeBookDetails(){
this.setState({ isBookDetailsOpen: false})
}
render(){
return(
<main>
<div class="container">
<Search state={this.state} setTableData={this.setTableData} />
<Table data={this.state.searchedBooks} openBookDetails={this.openBookDetails}/>
<Modal isBookDetailsOpen={this.state.isBookDetailsOpen} closeBookDetails={this.closeBookDetails} />
{/* <Modal /> */}
</div>
</main>
)
}
}
export default Update;

Related

react js state value always false

hello im trying to make a array that contain a objects and i want to map this array into two tables :thisis stricture of array:
0
:
{title: 'uml', agreement: false}
1
:
{title: 'react', agreement: false}
2
:
{title: 'laravel', agreement: false}
length
:
3
[[Prototype]]
:
Array(0)
and i have a checkbox that make agreement true or false .
but the problem is everytime the agreement is false.
i want to send objects to first table if is true and to seconde table if is false but everytime is send to first table with false agreement .
this is all of code:
some functions is just to show the values of states
import './App.css';
import { useRef, useState } from 'react';
function App() {
const [modules,setModules]=useState([])
const [agreement,setAgreement]=useState()
const [title,setTitle]=useState()
const checkbox=useRef()
function handlecheck(){
setAgreement(checkbox.current.checked)
}
function handlechange(event){
setTitle(event.target.value)
}
function ajouter(){
setModules([...modules,{title,agreement}])
}
function affich(){
return console.log(modules)
}
return (
<div className="App">
<section class="container cd-table-container">
<h2 class="cd-title">Insert Table Record:</h2>
<input onChange={(event)=>handlechange(event)} type="text" class="cd-search table-filter" data-table="order-table" placeholder="module name" />
<button className='ajouter' onClick={()=>ajouter()} >Ajouter</button>
<button className='ajouter' onClick={()=>affich()} >affich</button>
<input type={"checkbox"} ref={checkbox} onChange={(event)=>handlecheck(event)} />
<table class="cd-table table">
<thead>
<tr>
<th>modules regionaux</th>
</tr>
</thead>
<tbody>
{
modules.map((elm,index)=>{
if(elm.agreement=true){
return (<tr>
<td>{elm.title}</td>
</tr>)
}
})
}
</tbody>
</table>
<br></br>
<table class="cd-table table">
<thead>
<tr>
<th>modules non regionaux</th>
</tr>
</thead>
<tbody>
{
modules.map((elm,index)=>{
if(elm.agreement=false){
return (<tr>
<td>{elm.title}</td>
</tr>)
}
})
}
</tbody>
</table>
</section>
</div>
);
}
export default App;
so first you don't have to use (useRef) hock just use (useState) will be better , second you were using the if statement with the assign operator sign you should use the double equal sign or the triple sign this the full code
import './App.css';
import { useRef, useState } from 'react';
function App() {
const [modules, setModules] = useState([])
const [agreement, setAgreement] = useState()
const [title, setTitle] = useState()
function handlecheck(e) {
setAgreement(e)
}
function handlechange(event) {
setTitle(event.target.value)
}
function ajouter() {
setModules([...modules, { title, agreement }])
}
function affich() {
return console.log(modules)
}
return (
<div className="App">
<section class="container cd-table-container">
<h2 class="cd-title">Insert Table Record:</h2>
<input onChange={(event) => handlechange(event)} type="text" class="cd-search table-filter" data-table="order-table" placeholder="module name" />
<button className='ajouter' onClick={() => ajouter()} >Ajouter</button>
<button className='ajouter' onClick={() => affich()} >affich</button>
<input type={"checkbox"} onChange={(event) => handlecheck(event.target.checked)} />
<table class="cd-table table">
<thead>
<tr>
<th>modules regionaux</th>
</tr>
</thead>
<tbody>
{
modules.map((elm, index) => {
if (elm.agreement === true) {
return (<tr>
<td>{elm.title}</td>
</tr>)
}
})
}
</tbody>
</table>
<br></br>
<table class="cd-table table">
<thead>
<tr>
<th>modules non regionaux</th>
</tr>
</thead>
<tbody>
{
modules.map((elm, index) => {
if (elm.agreement === false) {
return (<tr>
<td>{elm.title}</td>
</tr>)
}
})
}
</tbody>
</table>
</section>
</div>
);
}
export default App;

Display a table in React using different components

I'm just starting to learn React and what I'm trying to do is to display a table. I'm doing it using 3 components: App.js, DisplayTable.js, and TableRows.js. The reason I'm doing it this way is that later on, I will need certain rows of the table to be displayed subject to different conditions. I don't get any error messages, but the table is not being displayed. This is my code:
App.js:
import Form from './components/Form'
import { useState } from 'react'
import TableDisplay from './components/TableDisplay'
const App = () => {
const [rows, setRows] = useState([
{
id:1,
description:'',
semester:'',
prefix:'ENG',
number:'368/371',
grade:'',
editing:''
},
{
id:2,
description:'',
semester:'',
prefix:'',
number:'',
grade:'',
editing:''
},
{
id:3,
description:'',
semester:'',
prefix:'',
number:'',
grade:'',
editing:''
},
])
return (
<div className="container">
<Form/>
<TableDisplay rows={rows}/>
</div>
);
}
export default App;
TableDisplay.js:
import TableRows from "./TableRows"
const TableDisplay = ({rows}) => {
return (
<>
{rows.map((row) => {
<TableRows key={row.id} row={row}/>
})}
</>
)
}
export default TableDisplay
TableRows.js:
import React from 'react'
const TableRows = ({row}) => {
return (
<div className="container">
<table className="table table-striped">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Description</th>
<th scope="col">Semester</th>
<th scope="col">Prefix</th>
<th scope="col">Number</th>
<th scope="col">Grade</th>
<th scope="col">Editing</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{row.id}</th>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
<tr>
<th scope="row">{row.id}</th>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
<tr>
<th scope="row">{row.id}</th>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
</tbody>
</table>
</div>
)
}
export default TableRows
You should remove bracket
const TableDisplay = ({ rows }) => {
return (
<>
{rows.map((row) => <TableRows key={row.id} row={row} />)}
</>
);
};
or add return
const TableDisplay = ({ rows }) => {
return (
<>
{rows.map((row) => {
return <TableRows key={row.id} row={row} />;
})}
</>
);
};

TypeError: this.state.edit is not a function

I want to create update operation on ReactJS
first I set edit button as
export default class Viewcustomer extends React.Component{
constructor(props) {
super(props)
this.state = {
customers:[]
}
}
componentDidMount() { /* lifecycle method*/
axios.get(`http://localhost:5001/customers/customerView`)
.then(res => {
const customers = res.data;
this.setState({customers});
})
}
onChange = (e) => {
this.setState(
{[e.target.name]: e.target.value}
)
}
edit=personId=>{
console.log(personId);
}
render(){
return(
<div>
<br/><br/>
<div className='container' style={container}>
<h1 style={h1}>Customer Details</h1>
<div className='col-md-12' style={colmd12}>
<br/><br/>
<div className="tbl-header" style={tblheader}>
<table className="table" style ={table} >
<thead className='thead' >
<tr className='tr' >
<th >Id</th>
<th>name</th>
<th>NIC</th>
<th>type</th>
<th>Delete</th>
<th>Update</th>
</tr>
</thead>
</table>
</div>
<div className="tbl-content" style={tblcontent}>
<table className="table" style ={table} >
<tbody>
{ this.state.customers.map(person =>
<tr className='td' style={td}>
<td>{person.Id}</td>
<td>{person.name}</td>
<td>{person.NIC}</td>
<td>{person.type}</td>
<td><Link to="update"><i class="fa fa-trash-o" style={iconstyle}></i></Link></td>
<td><Link to="update"><i class="fa fa-file" style={iconstyle} onClick={()=>this.state.edit(person.Id)}></i></Link></td>
</tr>)}
</tbody>
</table>
</div>
</div>
</div>
</div>
)
}
}
the update icon routes to Updatecustomer.jsx file.
then I set the edit fuction on Update.jsx file
import React from "react";
export default class Updatecustomer extends React.Component{
constructor(props){
super(props)
this.state={
update:[]
}
}
onChange = (e) => {
this.setState(
{[e.target.name]: e.target.value}
)
}
edit=personId=>{
console.log(personId);
}
render(){
return(
<div>
</div>
)
}
}
then my browser gives the following error: (TypeError: this.state.edit is not a function)
It is very big help if you have some ideas to fix this.
The edit function is not part of your state. Use onClick={()=>this.edit(person.Id)} instead.
Just remove state part and try
this.edit(person.id)

How to handle a onClick method in a component?

I believe I have my props setup correctly, but I keep getting "Cannot read property 'props' of undefined"
Child component:
const SearchResults = props => (
<div>
<div className="row" ><h4 style={{margin:"auto", marginBottom:"15px"}}>Search Results:</h4></div>
<table className="table table-striped">
<thead>
<tr>
{props.labels.map(label => ( <th key={label.Id}>{label.DisplayName}</th>))}
</tr>
</thead>
<tbody>
{ props.contracts.map((contract, i) => <tr key={i} data-id={contract.Id} onClick={() => this.props.handleContract(contract.Fields.filter(field => field.DataField==="IDXT001").map(field => field.DataValue))} className="clickable-row">{contract.Fields.map( docs => <td key={docs.Id}><span id={docs.DataField}>{docs.DataValue}</span></td>)}</tr> )}
</tbody>
</table>
</div>
)
Parent:
class SearchPage extends React.Component {
constructor(props) {
super(props);
this.state = {
labels: [],
contracts: [],
formValues:"",
pdfs:[],
id:"",
};
}
<SearchResults
labels={this.state.labels}
contracts={this.state.contracts}
pdfs={this.state.pdfs}
handleContract={this.onClick}
/>
You need to change this.props.handleContract to props.handleContract. this doesn't exist in your functional component.
const SearchResults = props => (
<div>
<div className="row" ><h4 style={{ margin: "auto", marginBottom: "15px" }}>Search Results:</h4></div>
<table className="table table-striped">
<thead>
<tr>
{props.labels.map(label => (<th key={label.Id}>{label.DisplayName}</th>))}
</tr>
</thead>
<tbody>
{props.contracts.map((contract, i) => <tr key={i} data-id={contract.Id} onClick={() => props.handleContract(contract.Fields.filter(field => field.DataField === "IDXT001").map(field => field.DataValue))} className="clickable-row">{contract.Fields.map(docs => <td key={docs.Id}><span id={docs.DataField}>{docs.DataValue}</span></td>)}</tr>)}
</tbody>
</table>
</div>
);
On your parent, make sure you have bound the onClick function as mentioned in the comments.
class SearchPage extends React.Component {
constructor(props) {
super(props);
this.state = {
labels: [],
contracts: [],
formValues: "",
pdfs: [],
id: ""
};
}
onClick = () => {
// Bind the function like this.
}
render() {
return (
<SearchResults
labels={this.state.labels}
contracts={this.state.contracts}
pdfs={this.state.pdfs}
handleContract={this.onClick}
/>
);
}
}

Find the row you have clicked in a table

I want to find the id of the row clicked in table.For instance I search for a book with the title 'Mastery' and there are 2 books with the same title but different authors.These books get shown in the table correctly. What I want to do is when I click on a particular book in the table it should open up a modal with the book details in input boxes, however when I click on any of the books the modal pops up with just the details of one of the books.
When I type in the search term ('Mastery') I get two suggestions which is the expected behaviour.
When I click on the suggested search term('Mastery') and hit enter or search button. All the books with that title('Mastery') gets populated in the Table. Also the expected behaviour.
Now when I click on the first instance of a book with title 'Mastery' this is what I get in my modal.
When I click on the second instance. I get this.
You realise that it is the same book that gets shown in the modal.
Expected Behaviour:
I want to be able to click on a book in the table and the book in that row get shown in the modal.
import React, { Component } from 'react';
import './Update.css';
// import Pace from 'react-pace-progress';
//CHILD COMPONENTS
import Search from '../../Search/Search';
import Modal from './Modal/Modal';
const Table = ({ data, openBookDetails }) => (
<table className="table table-hover">
<thead>
<tr className="table-primary">
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">ISBN</th>
<th scope="col">No. Of Copies</th>
</tr>
</thead>
<tbody>
{data.map(row =>
<TableRow key={row._id} row={row} openBookDetails={openBookDetails}/>
)}
{/* Remove key={row.id} inside TableRow because it is not used to set the data in the table */}
</tbody>
</table>
)
const TableRow = ({ row, openBookDetails }) => (
<tr className="table-light" onClick={openBookDetails}>
<th scope="row" >{row.title}</th>
<td >{row.author}</td>
<td >{row.isbn}</td>
<td >24</td>
</tr>
)
class Update extends Component{
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
setOfAllBooks: [],
searchedBooks: [],
isBookDetailsOpen: false,
searchForEmpty: true,
isDataFetching: true,
title: '',
author: '',
isbn: ''
};
}
setTableData = (searchedBook) => {
this.setState({searchedBooks: searchedBook});
}
openBookDetails = () => {
this.setState({ isBookDetailsOpen: true});
console.log(this.state.searchedBooks);
this.setState({ title: this.state.searchedBooks.title});
this.setState({ author: this.state.searchedBooks.author});
this.setState({ isbn: this.state.searchedBooks.isbn});
}
closeBookDetails = () => {
this.setState({ isBookDetailsOpen: false});
}
changeIsSearchForEmpty = () => {
this.setState({ searchForEmpty: !this.state.searchForEmpty });
}
changeIsDataFetching = () => {
this.setState({isDataFetching: !this.state.isDataFetching})
}
render(){
const showHideAlert = this.state.searchForEmpty ? 'alert alert-danger d-none' : 'alert alert-danger d-block';
// const showHideProgress1 = this.state.isDataFetching ? 'progress' : 'progress display-none';
const showHideProgress = this.state.isDataFetching ? 'progress progress-bar progress-bar-striped bg-success progress-bar-animated d-block' : 'progress-bar progress-bar-striped progress-bar-animated d-none';
const style= {
width: "100%",
height: "8px"
}
return(
<div>
{/* Uninstall react-pace-progress if not going to be used */}
{/* {this.state.isDataFetching ? <Pace color="#27ae60" height="0.5px"/> : null} */}
<div style={style}>
<div class={showHideProgress} role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style={style}></div>
</div>
<div className={showHideAlert}>
<strong>Sorry!</strong> You have to type in a search word/phrase.
</div>
<div className='px-3 pt-3'>
<Search
state={this.state}
setTableData={this.setTableData}
changeIsSearchForEmpty={this.changeIsSearchForEmpty}
changeIsDataFetching={this.changeIsDataFetching} />
<Table
data={this.state.searchedBooks}
openBookDetails={this.openBookDetails} />
<Modal
data={this.state.searchedBooks}
isBookDetailsOpen={this.state.isBookDetailsOpen}
closeBookDetails={this.closeBookDetails}
updateBookDetails={this.updateBookDetails}
grabTitle={this.grabTitle}
grabAuthor={this.grabAuthor}
grabISBN={this.grabISBN}
state={this.state} />
</div>
</div>
);
}
}
export default Update;
You're going to need some kind of ID or index on the tr element o your TableRow component. You can accomplish your goal without adding any extra react elements to your code, but your onClick function callback must be able to get the actual value.
If you take a look at the code below:
import React from "react";
import ReactDOM from "react-dom";
const data = [
{ id: "one", firstname: "john", lastname: "smith" },
{ id: "foo", firstname: "peter", lastname: "parker" }
];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
clicked_id: null
};
}
onClick = event => {
const id = event.currentTarget.getAttribute("data-rowid");
console.log(id);
this.setState({ clicked_id: id });
};
render() {
return (
<div>
<div>Clicked ID: {this.state.clicked_id}</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
{data.map(e => (
<tr key={e.id} data-rowid={e.id} onClick={this.onClick}>
<td>{e.id}</td>
<td>{e.firstname}</td>
<td>{e.lastname}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
you can see that the tr actually has a data-rowid element that is later used by the onClick method to extract the value. You can use other tags, I just chose that one for myself.
Edit to add:
If you want to take a look at the code above working, check out this codesandbox link:
https://codesandbox.io/s/4368l97lqx
Second edit:
You could just refactor your TableRow component to call the openBookDetails prop function with the parameter that you want:
class TableRow extends React.Component {
handleClick = (event) => {
event.stopPropagation();
const { row, openBookDetails } = this.props;
openBookDetails(row._id);
};
render() {
const { row } = this.props;
return (
<tr className="table-light" onClick={this.handleClick}>
<th scope="row">{row.title}</th>
<td>{row.author}</td>
<td>{row.isbn}</td>
<td>24</td>
</tr>
);
}
}
As per your code highlighted below:
<tbody>
{data.map(row =>
<TableRow key={row._id} row={row} openBookDetails={openBookDetails}/>
)}
{/* Remove key={row.id} inside TableRow because it is not used to set the data in the table */}
</tbody>
I think you need to pass the row values to openBookDetails like:
<TableRow key={row._id} row={row} openBookDetails={openBookDetails(row)}/>
and accordingly use the row values sent inside the openBookDetails function
Just wrote a similar code like this
`
<TableBody>
{rows.map((row, index) => (
<TableRow key={row.id} style={{
backgroundColor: deletingId == row.id ? 'salmon' : '',
color: deletingId == row.id ? 'white' : '',
}}>
<TableCell component="th" scope="row">
{index + 1}
</TableCell>
<TableCell align="right">{row.stockName}</TableCell>
<TableCell align="right">{row.stockCount}</TableCell>
<TableCell
align="right">{row.categoryResponseDTOS.length > 0 ? row.categoryResponseDTOS.map(category => {
return category.categoryName + ", "
}) : "---"} </TableCell>
<TableCell>
<ButtonGroup variant="contained" aria-label="outlined primary button group">
<Button color="warning" id={row.id}><EditIcon/> EDIT</Button>
<Button color="error" id={row.id} onClick={(e) => handleDelete(e)}>
<DeleteIcon/> DELETE</Button>
<Button color="success"> <ZoomInSharpIcon/> DETAIL</Button>
</ButtonGroup>
</TableCell>
</TableRow>
))}
</TableBody>
`
if you have a key you check the state you created if equals to it

Resources