When I press the AddAction button from the Addaction component, I want the popup to close. ?
in fact, if I reach the onCloseAddActionModal method in my component which is popup from AddAction component, my problem will be solved.
AddAction Component:
class AddAction extends React.Component {
constructor() {
super();
this.state = {
items: [{id:null, actiontype: null}],
error: null,
isLoaded: false,
selectId: null,
}
this.handleCheckChieldElement =
this.handleCheckChieldElement.bind(this); // set this, because you need get methods from CheckBox
}
componentDidMount = () => {
....
}
fetchAdd = (carid, offboardingactiontypeid) => {
...
}
handleCheckChieldElement = (id, e) => {
this.setState({selectId: id})
}
render() {
const items = this.state.items;
return (
<div>
<ul className="popupAddAction">
{
items.map((item) => {
return (
<li className="list" key={item.id}>
<input key={item.id} onClick=
{(e)
=>
this.handleCheckChieldElement(item.id,
e)} type="checkbox" />
{item.actiontype}
</li>
)
})
}
</ul>
<div className="popupAddAction--btn">
<button
onClick=
{ () =>
this.fetchAdd(this.props.id, this.state.selectId)}
className="btn btn-primary
popupAddAction--btn"
>
Add Action
</button>
</div>
</div>
);
}
}
export default AddAction;
Popup Component:
class OffBoarding extends Component {
this.state = {
openAddAction: false
};
onOpenAddActionModal = () => {
this.setState({ openAddAction: true });
};
onCloseAddActionModal = () => {
this.setState({ openAddAction: false });
};
render(){
return{
<div>
<Button className="btn btn-danger commentPlus" onClick=
{this.onOpenAddActionModal}> <FontAwesomeIcon icon=
{faPlus}/></Button>
</div>
{this.state.openAddAction ?
<div style={styles}>
<Modal open=
{this.state.openAddAction} onClose=
{this.onCloseAddActionModal} center>
<AddAction id=
{this.state.carid}
close=
{this.state.openAddAction}/>
</Modal>
</div> : null
}}
}}
You can simply pass the onCloseAddActionModal method as prop while rendering AddAction component from OffBoarding component. And then, you can call that passed function as prop on "Add Action" button click i.e.
So in you popup component, change this:
<AddAction id=
{this.state.carid}
close={this.state.openAddAction}/>
to this (passing function as prop):
<AddAction id=
{this.state.carid}
close={this.state.openAddAction}
closeDialog={this.onCloseAddActionModal}/>
And then in your AddAction component, change this:
<button
onClick={() =>
this.fetchAdd(this.props.id, this.state.selectId)}
className="btn btn-primary popupAddAction--btn">
Add Action
</button>
to this (calling function passed as prop in previous step):
<button
onClick=
{() =>{
this.fetchAdd(this.props.id, this.state.selectId);
this.props.closeDialog();
}}
className="btn btn-primary popupAddAction--btn">
Add Action
</button>
If openAddAction flag is true then only addaction component will display right. Instead of open and close modal add below code to modal and in add action modal in fetch method set openAddAction to false. in your code you have communication from child to parent but you are trying to close modal based on child but modal exist in parent so make a communication to parent to child for that pass function through component
<Modal isOpen = {this.state.openAddAction} center>
<AddAction id= {this.state.carid}
closeModa={this.onCloseAddActionModal} />
</Modal>
In addAction modal you have to add like this
fetchAdd = (carid, offboardingactiontypeid) => {
this.setState({openAddAction:false});
this.props.onCloseAddActionModal();
}
Call this closeModal method in fetchAdd method
Related
I'm trying to submit a Form from Child Component through a button placed in Parent. The Parent is a Slide which has a Next Button and I display my Child Component in one page of a Slide.
What I want to do is: When I press next button from Parent I have to go to the next slide and also to submit the Form Child.
code:
Parent Component:
import Slider from "react-slick";
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
errors: {}
}
next() {
this.slider.slickNext();
}
previous() {
this.slider.slickPrev();
}
render() {
const settings = {
dots: true,
infinite: false
};
return (
<div>
<Slider ref={c => (this.slider = c)} {...settings}>
<div style={{ textAlign: "center" }} key={1} >
<Child /> //this is the Child I want to submit once I press Next Button
<Button id="btn" className="btn btn-primary float-right " type="submit" onClick={this.next}> Next</Button> //this is the next button which sould trigger the submit from Child
</div>
<div key={2} >
<Page2/>
<Button className="btn btn-primary float-right " id="btn" onClick={() => this.finish()>Finish</Button>
<Button id="btn" className="btn btn-primary float-right " onClick={this.previous}> Previous</Button>
</div>
</Slider>
</div>
);
}
}
Child Component:
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
info: '',
infoID: 0,
valid:false,
errors: {}
}
this.handleValidSubmit = this.handleValidSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
handleValidSubmit() {
if(valid){
localStorage.setItem("infoID",this.state.infoID);
this.sendIDToServerAPI(infoID);
}else {
localStorage.setItem("infoID",this.state.infoID);
this.saveNewInfoAPI(info);
}
}
render() {
return (
<div }>
<Container fluid >
<Row >
<Col >
<AvForm id="avForm" onValidSubmit={this.handleValidSubmit} onInvalidSubmit=}>
<Label >Info</Label>
<AvField onChange={this.onChange} name="email" placeholder=""Email/>
........
.......
</AvForm>
</Col>
</Row>
</Container>
</div>
);
}
}
I tried to minimise the components because they are too large, I hope is clear enough what I try to do. Can anyone please help me with a solution/idea? I tried to Google it but couldn't find anything helpful.
Thanks a lot
What you can do is keep a state(a flag) something like submit which will initially be false.
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
submit: false
....rest of your code
}
// function to update the submit state
const setFormSubmit = () => this.setState({submit: true})
}
Now you call setFormSubmit on next button click
Now you can pass this state(submit) to your child component.
and whenever the submit is true you will trigger the method to submit the form
You can do this by using componentDidUpdate in your child component.
componentDidUpdate(prevProps, prevState) {
// if prev submit and current submit prop is unequal and the submit is true
if ((prevProps.submit !== this.props.submit) && this.props.submit) {
// Call method to submit form
}
}
I'm trying to pass data into Modal (bootstrap) popup and display some data.
I have a list of orders with a button 'display info', and every button that i press should display on the popup (Modal) diffrent data.
My question is how should i pass the data to the Modal?
this line <Button variant="primary" onClick={() => {this.handleModal(index)}}> Items info</Button> should trigger the Modal. In the handleModal function it passes the order index. And then i update the index on the setState of the handleModal function.
The Modal open but nothing passes to it.
I'm not sure that this is the correct way of doing it.
Also the Modal is inside the loop of the filteredOrders, should i move the Modal outside the loop?
And if yes, how should i do that and where?
import React, {useState} from 'react';
import './App.scss';
import {createApiClient, Item, Order} from './api';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import 'bootstrap/dist/css/bootstrap.min.css'
export type AppState = {
orders?: Order[],
search: string;
show:boolean;
item?: Item,
order_id: number,
}
const api = createApiClient();
export class App extends React.PureComponent<{}, AppState> {
state: AppState = {
search: '',
show:false,
order_id: 0,
};
searchDebounce: any = null;
async componentDidMount() {
this.setState({
orders: await api.getOrders()
});
}
async getItem(itemID: string){
this.setState({
item: await api.getItem(itemID)
});
}
render() {
const {orders} = this.state;
return (
<main>
<h1>Orders</h1>
<header>
<input type="search" placeholder="Search" onChange={(e) => this.onSearch(e.target.value)}/>
</header>
{orders ? <div className='results'>Showing {orders.length} results</div> : null}
{orders ? this.renderOrders(orders) : <h2>Loading...</h2>}
</main>
)
}
handleModal(index: number)
{
this.setState({
show:true,
order_id: index,
})
}
handleClose () {
this.setState({show: false})
}
renderOrders = (orders: Order[]) => {
const filteredOrders = orders
.filter((order) => (order.customer.name.toLowerCase() + order.id).includes(this.state.search.toLowerCase()));
const requiredItem = this.state.order_id;
const modelData = filteredOrders[requiredItem];
return (
<div className='orders'>
{filteredOrders.map((order,index) => (
<div className={'orderCard'}>
<div className={'generalData'}>
<h6>{order.id}</h6>
<h4>{order.customer.name}</h4>
<h5>Order Placed: {new Date(order.createdDate).toLocaleDateString()}</h5>
</div>
<div className={'fulfillmentData'}>
<h4>{order.itemQuantity} Items</h4>
<img src={App.getAssetByStatus(order.fulfillmentStatus)}/>
{order.fulfillmentStatus !== 'canceled' &&
<a href="#" onClick={() => this.ChangeStatus(order)}>Mark
as {order.fulfillmentStatus === 'fulfilled' ? 'Not Delivered' : 'Delivered'}</a>
}
</div>
<div className={'extraData'}>
<Button variant="primary" onClick={() => {this.handleModal(index)}}> Items info</Button>
<Modal show={this.state.show} >
{/*{console.log(modelData)}*/}
{/*<Modal.Header closeButton>*/}
{/* <Modal.Title>Item Info</Modal.Title>*/}
{/*</Modal.Header>*/}
<Modal.Body>
{ console.log(modaelData) }
</Modal.Body>
<Modal.Footer>
<Button onClick={() =>{ this.handleClose()}}>
Close
</Button>
</Modal.Footer>
</Modal>
</div>
<div className={'paymentData'}>
<h4>{order.price.formattedTotalPrice}</h4>
<img src={App.getAssetByStatus(order.billingInfo.status)}/>
</div>
</div>
))}
</div>
)
};
}
export default App;
I don't think you need to pass data to the Modal, but rather compose the Modal with the data in the first place. It is currently empty. Then you can continue to hide/show the complete Modal with handleModal.
I've rendered a popup for every product ,so when some one clicks the icon a popup is going to come.
The problem now i'm facing is that on clicking any product all the popup is getting open.since it is inside map function .
My requirement here is to only open the popup related to product and close all the other popup.
export default class index extends Component {
state = {
isPopoverOpen: false,
};
openPopup = () => {
this.setState({ isPopoverOpen: !this.state.isPopoverOpen });
}
render() {
return (
this.props.dataArray.map((x, index) => {
<button onClick={() => this.openPopup()}> Open </button>
{x.id != index ?
<PopUp
isPopoverOpen={this.state.isPopoverOpen}
/> : '' }
}
)
}
}
//Code for Popup Component
export class index extends Component {
state = {
isPopoverOpen: false,
};
// add business user
onClickOutside = async () => {
this.setState({ isPopoverOpen: false })
};
ClickedPop = () => {
this.setState({ isPopoverOpen: !isPopoverOpen })
}
render() {
const { isPopoverOpen } = this.state;
return (
<PopUpWrapper>
<Popover
isOpen={this.props.isPopoverOpen}
position={['right']}
padding={0}
onClickOutside={() => console.log("<<<< Clicked Outside! >>>>>")}
content={({ position, targetRect, popoverRect }) => (
<PopUpWrapper>
<div
style={{ backgroundColor: 'white', opacity: 0.7 }}
//onClick={() => this.setState({ isPopoverOpen: !isPopoverOpen })}
>
Hi! I'm popover content. Here's my position: {position}.
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Add the bucket" aria-describedby="basic-addon2" />
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button">+</button>
</div>
</div>
</div>
</PopUpWrapper>
)}
>
<div>
</div>
</Popover>
</PopUpWrapper>
)
}
}
I am not react expert, but it may b due to, you are taking state as globally in this class.
creating your state inside onclick handler may solve your problem.
I am new to StackOverflow so pardon me for bad wording of the problem.
I am learning React currently and as a small project, I am creating a course-search app which will filter the courses on the basis of input entered by the user from a JSON file and display them.
I have added modals to each card component which should open on the click of a button within the card.
The problem I'm facing is that when I click the button, it only opens the modal first clicked one, and none else.
Here is the code:
import MyModal from '../Modal/Modal'
import courseList from "./courses.json";
class App extends Component {
state = {
search: "",
modal: false,
};
selectModal = (id) => {
this.setState({
modal: {
[id]: !this.state.modal
}
})
}
rendercourse = course => {
var dep = course.dep;
return (
<div className="col-md-3" style={{ marginTop: "20px" }}>
<Card className="card">
<CardBody className={dep}>
<CardTitle title={course.id}>
{course.code}
</CardTitle>
<CardText className="title">{course.name}</CardText>
<p className="ic">{course.ic}</p>
Units: {course.unit}<br />
<button onClick= {
this.selectModal.bind(this, course.code)}>More Info</button>
</CardBody>
<MyModal
displayModal={this.state.modal[course.code]}
coursename={course.name}
coursecode={course.code}
courseic={course.ic}
coursedep={course.dep}
courseunit={course.unit}
closeModal={this.selectModal} />
</Card>
</div>
);
};
onchange = e => {
this.setState({ search: e.target.value });
};
render() {
const { search } = this.state;
const filteredcourses = courseList.filter(course => {
return course.name.toLowerCase().indexOf(search.toLowerCase()) !== -1;
});
return (
<div className="flyout">
<main style={{ marginTop: "4rem" }}>
<div className="container">
<div className="row">
<div className="col-12">
<center>
<h3>
Search for a course
</h3>
</center>
</div>
<div className="col">
<Input
label="Enter the name of the course"
icon="search" className="in"
onChange={this.onchange}
/>
</div>
<div className="col" />
</div>
<div className="row">
{filteredcourses.map(course => {
return this.rendercourse(course);
})}
</div>
</div>
</main>
</div>
);
}
}
export default App;
And here is the modal component:
const MyModal = props => {
function displayInfo () {
return (
<div>
<div>{props.coursename}</div>
<div>{props.courseic}</div>
<div>{props.courseunit}</div>
<div>{props.coursedep}</div>
<div>{props.coursecode}</div>
</div>
);
}
function closeModal (e) {
e.stopPropagation()
props.closeModal()
}
let modal = (
<div
className="modal"
onClick={ closeModal }>
<div className="modal-content"
onClick={ e => e.stopPropagation() }>
<span
className="close"
onClick={ closeModal }>×
</span>
<div className="modal-flex">
{displayInfo()}
</div>
</div>
</div>
)
return ( props.displayModal ? modal : null);
}
export default MyModal;
I want the card-specific modal to open up whenever the button is clicked.
Your selectModal function doesn't flip the state of each modal, but only that of the first one.
If any modal is defined in the state (be it the first or any other modal), this.state.modal will evaluate to true. (an object with at least some content is true)
To allow all your modals to be opened at the same time simply flip the value of the modal element in your modal state object.
this.state.modal[id] is undefined by default, which will evaluate to boolean false.
selectModal = (id) => {
this.setState({
modal: {
[id]: !this.state.modal[id]
}
})
}
If you'd rather only open one modal at a time, you should simply store the id of your modal in the state instead of tracking all modals in there:
selectModal = (id) => {
this.setState({
modal: id
})
}
To close a modal change your closeModal prop to pass undefined/false instead of the modal ID and change your modal display prop so it will perform the check whether your modal state and the coursecode match:
this.state.modal === course.code
I am making a dashboard component which displays rendered previews and code for HTML snippets. Inside of the dashboard component I am mapping the array of snippets using .map. Each mapped snippet is going to have a delete function (already built) and an update function.
For the update function to work each snippet has it's own child modal component. I need to pass the ID of the snippet to the modal component where I can combine the ID with the new content before updating the database and state.
However, I'm making a mistake somewhere as I pass the ID as props to the modal.
.map used inside of my Dashboard.js Dashboard class component.
{this.state.snippets.map(snippet => (
<>
<div key={snippet._id} className="holder--pod">
<div className="content">
<div className="content__snippet-preview">
Snippet preview
</div>
<div className="content__body">
<h4>{snippet.name}</h4>
<p>{snippet.details}</p>
<p>{snippet._id}</p> //THIS WORKS
<pre>
<code>{snippet.content}</code>
</pre>
</div>
<div className="content__button">
<button onClick={this.handleDelete(snippet._id)}>
Delete
</button>
<button type="button" onClick={this.showModal}>
Open
</button>
</div>
</div>
</div>
<Modal
sid={snippet._id} //PASS ID HERE
show={this.state.show}
handleClose={this.hideModal}
></Modal>
</>
))}
This renders the snippets below (3 snippet pods, with their database ID included).
The open button opens the modal (Modal.js) below.
import React, { Component } from 'react'
import api from '../api'
export default class Modal extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
details: '',
content: '',
message: null,
}
}
handleInputChange = event => {
this.setState({
[event.target.name]: event.target.value,
})
}
handleClick = id => event => {
event.preventDefault()
console.log(id)
}
render() {
const { sid, show, handleClose } = this.props
console.log(sid)
const showHideClassName = show ? 'modal display-flex' : 'modal display-none'
return (
<div id="Modal" className={showHideClassName}>
<div id="modal-main">
<h4>Edit snippet {sid}</h4>
<form>
Name:{' '}
<input
type="text"
value={this.state.name}
name="name"
onChange={this.handleInputChange}
/>{' '}
<br />
Details:{' '}
<input
type="text"
value={this.state.details}
name="details"
onChange={this.handleInputChange}
/>{' '}
<br />
Content:{' '}
<textarea
value={this.state.content}
name="content"
cols="30"
rows="10"
onChange={this.handleInputChange}
/>{' '}
<br />
<button onClick={this.handleClick(sid)}>TEST ME</button>
</form>
<button onClick={handleClose}>Close</button>
{this.state.message && (
<div className="info">{this.state.message}</div>
)}
</div>
</div>
)
}
}
The console.log just under the render actually pastes the correct 3 ID's the console.
However, calling the ID (sid) within the Modal.js return will only show the last snippet ID, no matter which Modal I open. The same goes for pushing that ID to the handleClick function where I intend to combine the ID with an update package.
Solution below as initiated by HMR in the comments.
The problem was all the modals were showing and just the last one was visible.
Fixed by moving the modal out of the .map and instead updating the ID from within the .map to the state and passing the state ID to a new nested component within the modal.
Also switched to using dynamic CSS to show and hide the modal based on the state.
Dashboard.jsx
export default class Snippets extends Component {
constructor(props) {
super(props)
this.showModal = React.createRef()
this.state = {
snippets: [],
show: false,
sid: '',
}
}
handleDelete = id => event => {
event.preventDefault()
api
.deleteSnippet(id)
.then(result => {
console.log('DATA DELETED')
api.getSnippets().then(result => {
this.setState({ snippets: result })
console.log('CLIENT UPDATED')
})
})
.catch(err => this.setState({ message: err.toString() }))
}
handleModal = id => {
this.setState({ sid: id })
this.showModal.current.showModal()
}
//<div id="preview">{ReactHtmlParser(snippet.content)}</div>
render() {
return (
<>
<Modal ref={this.showModal} handleClose={this.hideModal}>
<ModalUpdate sid={this.state.sid} />
</Modal>
<div className="Dashboard">
<div className="wrapper">
<div className="container">
<div className="holder">
<div className="content">
<div className="content__body">
<h3>Dashboard</h3>
</div>
</div>
</div>
<div className="break"></div>
{this.state.snippets.map(snippet => (
<div key={snippet._id} className="holder--pod">
<div className="content">
<div className="content__snippet-preview">
Snippet preview
</div>
<div className="content__body">
<h4>{snippet.name}</h4>
<p>{snippet.details}</p>
<p>{snippet._id}</p>
<pre>
<code>{snippet.content}</code>
</pre>
</div>
<div className="content__button">
<button onClick={this.handleDelete(snippet._id)}>
Delete
</button>
<button
type="button"
onClick={() => this.handleModal(snippet._id)}
>
Open
</button>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</>
)
}
Modal.jsx
import React, { Component } from 'react'
export default class Modal extends Component {
constructor(props) {
super(props)
this.state = {
show: false,
}
}
showModal = () => {
this.setState({ show: true })
}
hideModal = () => {
this.setState({ show: false })
}
render() {
return (
<div
id="Modal"
style={{ display: this.state.show === true ? 'flex' : 'none' }}
>
<div id="modal-main">
<h4>Edit snippet </h4>
{this.props.children}
<button onClick={() => this.hideModal()}>Close</button>
</div>
</div>
)
}
}
ModalUpdate.jsx
import React, { Component } from 'react'
export default class ModalUpdate extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
details: '',
content: '',
message: null,
}
}
// handleInputChange = event => {
// this.setState({
// [event.target.name]: event.target.value,
// })
// }
// handleClick = id => event => {
// event.preventDefault()
// console.log(id)
// }
render() {
return <h4>ID = {this.props.sid}</h4>
}
}
I am not sure about the handleDelete function,. but replacing the line should solve the issue probably
<button onClick={() => this.handleDelete(snippet._id)}>
One potential issue is the this.handleDelete(snippet._id) will fire immediately rather than onClick, so you will need to add an anonymous function in the event listener:
() => this.handleDelete(snippet._id)
instead of
this.handleDelete(snippet._id)