multiple modal in .map - javascript

i am in a .map an i want to creat on modal in each div generate by the .map
return (
{this.state.DataBook.map(function (item, i) {
return ( <div>
<Button color="danger" onClick={this.toggleModal}>test Modal</Button>
<Modal isOpen={this.state.modal} toggle={this.toggleModal} className={this.props.className} external={externalCloseBtn}>
<ModalHeader>Modal title</ModalHeader>
<ModalBody>
<b>{item.nom}</b><br />
Lorem ipsum
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.toggleModal}>Do Something</Button>{' '}
</ModalFooter>
</Modal>
</div>)},this)}
In order to do that i call toggleModal
toggleModal() {
this.setState({
modal: !this.state.modal
});
}
with this i have all my modal with the same content i don't know how to fix it.
Any idea?

Here's a working codeSandbox running this : https://codesandbox.io/s/rmxp8nxm74
To be able to individually identify events coming from JSX made by mapping an array, you have to pass an identifier to the called openModal method :
openModal = id => {
this.setState({ openedModal: id });
};
closeModal = () => {
this.setState({ openedModal: null });
};
render() {
return this.state.DataBook.map((item, i) => (
<div key={item.id}>
<Button color="danger" onClick={() => this.openModal(item.id)}>
test Modal
</Button>
<Modal
isOpen={this.state.openedModal === item.id}
toggle={this.closeModal}
>
<ModalHeader>Modal title</ModalHeader>
<ModalBody>
<b>{item.nom}</b>
<br />
Lorem ipsum
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.closeModal}>
Do Something
</Button>
</ModalFooter>
</Modal>
</div>
));
}
Edit : I created distinct open/close methods so you'll be able to easily close the active modal with an external call (no param to pass).
Edit 2 : You have also an alternative option : having a single dynamic Modal passing it a state set onClick (could be better in performance but harder to manage different click events in modal)

You can have activeModalIndex in your state which can be used to show the actual modal. Check the activeModalIndex when you open your modal.
this.state.DataBook.map(function (item, index) {
return (
<div>
<Button color="danger" onClick={() => this.toggleModal(index)}>test Modal</Button>
<Modal isOpen={(this.state.activeModalIndex === index) && this.state.modal} toggle={()=> this.toggleModal(index)} className={this.props.className} external={externalCloseBtn}>
...
</Modal>
</div>
You can toggle modal
toggleModal(activeIndex) {
this.setState({
modal: !this.state.modal,
activeModalIndex: activeIndex
});
}
Hope this helps.

Related

how to open modal2 from a button of modal1 in react

I have two modals one is Login modal and another is Signup modal now I want to open signup modal by clicking on a button which is present in login modal, I tried many different ways but not able to achieve it, also I tried to make a signupModalSwitch with useReducer and by making different function calls but it was saying against hooks rule, I am very new to react not able to figure out how to do it. Thanks in Advance :)
modal1 ->
function LoginModal() {
const [open, setOpen] = React.useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button
onClick={handleOpen}
variant="outlined"
className={classNames(
classes.textNeonGreen,
classes.outlinedNeonGreen,
classes.navButton
)}
classes={{ disabled: classes.disabled }}
>
<Typography noWrap>Login</Typography>
</Button>
<Modal
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<LoginCard LoginClose={handleClose} />
</Fade>
</Modal>
</div>
);
}
function LoginCard({ LoginClose }) {
return (
<Card className={classes.modalCard}>
<span>
<button
onClick={LoginClose}
type="button"
className="close px-2 pt-2"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</span>
<CardContent className={classes.modalCardContent}>
<Typography variant="h4" className={classes.modalHeading}>
<b>Login</b>
</Typography>
<Account>
<Login />
</Account>
<div>
<Grid
container
direction="column"
justify="center"
alignItems="center"
>
<p className="mb-2 mt-4 text-center">
Don't have Account?
</p>
//###### BY CLICKING THIS BUTTON I WANT TO OPEN MY SIGNUP MODAL #######//
<button
className="btn btn-outline-success btn-block btn-md"
onClick={() => {
LoginClose();
signupModalSwitch(null,{type:'open'})
}
}
>
Signup
</button>
</Grid>
</div>
</CardContent>
</Card>
);
}
MODAL2 ->
function signupModalSwitch(state, action){
switch(action.type){
case 'open':
return { open: true }
case 'close':
return { open: false }
default:
console.log(action);
}
}
function SignupModal() {
const classes = useStyles();
const [state, dispatch] = useReducer(signupModalSwitch, { open: false })
function handleOpen() {
dispatch({type: "open"});
}
function handleClose() {
dispatch({type: "close"});
};
return (
<div>
<Button
onClick={handleOpen}
variant="outlined"
className={classNames(
classes.textNeonGreen,
classes.outlinedNeonGreen,
classes.navButton
)}
classes={{ disabled: classes.disabled }}
>
<Typography noWrap>Signup</Typography>
</Button>
<Modal
className={classes.modal}
open={state.open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={state.open}>
<SignupCard signupClose={handleClose} />
</Fade>
</Modal>
</div>
);
}
function SignupCard({ signupClose }) {
return (
<Card className={classes.modalCard}>
<span>
<button
onClick={signupClose}
type="button"
className="close px-2 pt-2"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</span>
<CardContent className={classes.modalCardContent}>
<Typography variant="h4" className={classes.modalHeading}>
<b>Signup</b>
</Typography>
<Signup />
</CardContent>
</Card>
);
}
A better way to do this would be to create a container and move all the logic to the container. Your login and sign up modals should be dumb components, i.e, do not maintain the state in these components.
In your container, keep two states, openLoginModal and openSignupModal.
const [openLoginModal, setOpenLoginModal] = useState(false)
const [openSignupModal, setOpenSignupModal] = useState(false)
You can pass these as props to your login and signup components. When you click the button in the login modal, you will need one more function that closes login modal and opens sign up modal. You can pass this as prop as well.
const onSignupButtonClick = () => {
setOpenLoginModal(false)
setOpenSignupModal(true)
}
You can conditionally render login and signup modal based on the state values.

How to tigger onClick function of another component element

I created one class base component and one functional component. There are three buttons inside the functional component and I called that component to my class based component.
The functional component:
function PanelButton(props){
return (
<div>
<Button.Ripple
color="success"
type="submit"
style={{margin:"5px", width:"110px"}}
>
Submit
</Button.Ripple>
<Button.Ripple
color="primary"
id="clearButton"
type="button"
style={{margin:"5px", width:"110px"}}
>
Clear
</Button.Ripple>
<Button.Ripple color="danger" type="button" style={{margin:"5px", width:"110px"}}>
Close
</Button.Ripple>
</div>
)
}
export default PanelButton;
The class base component, in which I imported the functional component into:
import PanelButton from '../../components/customzied/PanelButton';
class TicketNew extends React.Component{
state = {
alertOption:[],
}
clickClear = () => {
console.log("ok");
}
render() {
const rqst = this.state.rquirdSate;
return (
<Card>
<Formik>
{ ({ errors, touched}) => (
<div>
<Form onSubmit={handleSubmit}>
<CardHeader>
<PanelButton />
</CardHeader>
<CardBody>
<Row />
</CardBody>
</Form>
</div>
)}
</Formik>
</Card>
)
}
}
export default TicketNew;
When I click the button(id = "clearButton") from functional component, I need to run the Click clear function in Class component.
You can pass onClick callback handlers as props to PanelButton to attach to each button's onClick prop. Pass clickClear as callback for clear button.
PanelButton
function PanelButton(props) {
return (
<div>
...
<Button.Ripple
color="primary"
id="clearButton"
type="button"
style={{ margin: "5px", width: "110px" }}
onClick={props.onClear} // <-- attach callback to button's onClick handler
>
Clear
</Button.Ripple>
...
</div>
);
}
TicketNew
class TicketNew extends React.Component {
state = {
alertOption: []
};
clickClear = () => {
console.log("ok");
};
render() {
const rqst = this.state.rquirdSate;
return (
<Card>
<Formik>
{({ errors, touched }) => (
<div>
<Form onSubmit={handleSubmit}>
<CardHeader>
<PanelButton onClear={this.clickClear}/> // <-- pass this.clickClear to onClear prop
</CardHeader>
<CardBody>
<Row></Row>
</CardBody>
</Form>
</div>
)}
</Formik>
</Card>
);
}
}
You can pass the clickClear function as props to the PanelButton component. The PanelButton code will look like the following,
function PanelButton(props){
return(
<div>
<Button.Ripple color="success" type="submit" style={{margin:"5px", width:"110px"}}>
Submit
</Button.Ripple>
<Button.Ripple color="primary" id="clearButton" onClick={props.onClickCallback} type="button" style={{margin:"5px", width:"110px"}}>
Clear
</Button.Ripple>
<Button.Ripple color="danger" type="button" style={{margin:"5px", width:"110px"}}>
Close
</Button.Ripple>
</div>
)
}
And the TicketNew code will look like this,
...
<CardHeader>
<PanelButton onClickCallback={this.clickClear.bind(this)} />
</CardHeader>
...

How to pass data from a table row and pass it to another component when it renders?

I have a component that shows a table and one of its columns have a field Actions which has buttons (view, edit, delete etc.). On button click, I need to render another component (component is a popup) and pass the data from the table so that it displays the data in some form which I need to further add.
I have managed to get the current data from its row by passing in onClick. I tried to use state for another component to render but it didn't work out. I'm using Semantic-UI React components to display the button with animations.
Here is the code that has the table,
const MainContent = () => {
const [actions, setActions] = useState(false);
const handleView = (rowData) => {
console.log(rowData);
setActions(true);
if (actions == true) return <ParentView />;
};
....
....
const contents = (item, index) => {
return item.routeChildEntry ? (
<>
<tr key={index}>
<td>{item.appName}</td>
<td></td>
<td>{item.createdTs}</td>
<td>{item.pattern}</td>
<td></td>
<td></td>
<td></td>
<td>
<Button animated onClick={() => handleView(item)}>
<Button.Content visible>View</Button.Content>
<Button.Content hidden>
<Icon name="eye" />
</Button.Content>
</Button>
</td>
</tr>
{item.routeChildEntry.map(routeContents)}
</>
) : (
....
....
....
);
};
return (
<div>
....
{loading ? (
<div className="table-responsive">
<table className="table">
<thead>
<tr>
<th>AppName</th>
<th>Parent_App</th>
<th>Created_Date</th>
<th>Req_Path</th>
<th>Resp_Code</th>
<th>Resp_Content_Type</th>
<th>Resp_Delay</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{data.map((routes, index) => {
return routes.map(contents, index);
})}
</tbody>
</table>
</div>
) : (
....
....
)}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default MainContent;
Below is the component to render on button click,
import React from "react";
import Popup from "reactjs-popup";
import { Icon } from "semantic-ui-react";
const Parent = (props) => {
return (
<Popup trigger={<Icon link name="eye" />} modal closeOnDocumentClick>
<h4>in parent</h4>
</Popup>
);
};
export default Parent;
How can I render the other component and pass data to it on button click?
Data can be passed to other components as props.
For example, if your component is <ParentView />, and the data you are passing is contained in the variable rowData, you can pass it as:
<ParentView dataPassedByAkhil = {rowData}/>
Then in your ParentView component,
export default function ParentView({dataPassedByAkhil}) {
console.log(dataPassedByAkhil);
Alternatively, you can accept the data as props as below
export default function ParentView(props) {
console.log(props.dataPassedByAkhil);
If you want to open and close another popup, you can pass a state just like above.
<PopupComponent open={stateSetForPopupByParent}/>
Example of popup using state.
Updated link above with how to pass data to the dialog from rows of buttons
Here is the full code:
export default function FormDialog() {
const [open, setOpen] = React.useState(false);
const [valueofRow, setValueOfRow] = React.useState();
const handleClickOpen = (value) => {
setValueOfRow(value);
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button
variant="outlined"
color="primary"
onClick={() => {
handleClickOpen("John");
}}
>
Row 1 - value is John
</Button>
<br />
<br />
<Button
variant="outlined"
color="primary"
onClick={() => {
handleClickOpen("Sally");
}}
>
Row 2 Value is Sally
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="edit-apartment"
>
<DialogTitle id="edit-apartment">Edit</DialogTitle>
<DialogContent>
<DialogContentText>Dialog fired using state</DialogContentText>
<h1>{valueofRow} was clicked and passed from the row</h1>
<TextField
autoFocus
margin="dense"
id="field"
label="some field"
type="text"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="secondary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Submit
</Button>
</DialogActions>
</Dialog>
</div>
);
}

How can I pass data from one card, in a list of cards, to a dialog?

I want to add a confirmation step for my CRUD application using a Material-UI dialog, but I don't seem to be able to pass any data to my dialog.
I have a list/grid of cards that I've created using .map(), each of which contains data I pulled from a MongoDB document. I'm able to delete the documents/cards from my app with just a button, but now I'd like to add a confirmation step to the deletion process, using a Material-UI dialog. To do that, I need to pass data from the cards to the dialog (if that's the correct phrasing for what I'm trying to do). I can't figure out how to do that.
I've tried passing the data using this.props.match.params.id, this.id, this._id, this.oneSavedArticle.id, and this.props.oneSavedArticle.id, but they all have either returned an error or undefined.
Here is my delete function:
handleDelete = id => {
console.log(id);
API.deleteArticle(this)
.then(res => console.log(res.data))
.catch(err => console.log(err));
// this.props.history.push("/saved");
};
Here is my dialog:
<div>
<Button
color="secondary"
variant="outlined"
onClick={this.handleClickOpen}
>
DELETE
</Button>
<Dialog
open={this.state.open}
TransitionComponent={Transition}
keepMounted
onClose={this.handleClose}
aria-labelledby="alert-dialog-slide-title"
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle>
{"Delete this article?"}
</DialogTitle>
<Divider variant="middle" />
<DialogActions>
<Button onClick={this.handleClose} color="secondary">
NO
</Button>
<Button
onClick={() => this.handleDelete(this.props)}
color="primary"
>
YES
</Button>
</DialogActions>
</Dialog>
</div>
Here is my API route:
deleteArticle: function(id) {
return axios.delete("/api/articles/" + id);
}
Here is where and how I've implemented the dialog into my list of cards:
{this.state.savedArticles.length ? (
<Grid>
{this.state.savedArticles.map((oneSavedArticle, i) => (
<Card style={savedArticleCard} key={i}>
<Typography variant="h5">{oneSavedArticle.headline}</Typography>
<Divider variant="middle" />
<Typography>{oneSavedArticle.snippet}</Typography>
<a href={oneSavedArticle.web_url} style={linkStyles}>
<Button style={buttonStyles}>READ IT</Button>
</a>
<DeleteDialogue {...this.props} />
</Card>
))}
</Grid>
As you can expect, I'd just like to be able to pass the data from to card to the dialog so I can get my delete function functioning correctly.
If I've left out any info, or haven't provided enough code, or haven't explained something clearly enough, please let me know.
Many thanks ahead of time!
If I understand correctly, the DeleteDialogue is the dialog component that you are talking about.
If so, try passing a specific prop to the dialog and use it in the dialog.
Something like that:
{this.state.savedArticles.length ? (
<Grid>
{this.state.savedArticles.map((oneSavedArticle, i) => (
<Card style={savedArticleCard} key={i}>
<Typography variant="h5">{oneSavedArticle.headline}</Typography>
<Divider variant="middle" />
<Typography>{oneSavedArticle.snippet}</Typography>
<a href={oneSavedArticle.web_url} style={linkStyles}>
<Button style={buttonStyles}>READ IT</Button>
</a>
<DeleteDialogue id={oneSavedArticle.id} />
</Card>
))}
</Grid>
And in the dialog:
<div>
<Button
color="secondary"
variant="outlined"
onClick={this.handleClickOpen}
>
DELETE
</Button>
<Dialog
open={this.state.open}
TransitionComponent={Transition}
keepMounted
onClose={this.handleClose}
aria-labelledby="alert-dialog-slide-title"
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle>
{"Delete this article?"}
</DialogTitle>
<Divider variant="middle" />
<DialogActions>
<Button onClick={this.handleClose} color="secondary">
NO
</Button>
<Button
onClick={() => this.handleDelete(this.props.id)}
color="primary"
>
YES
</Button>
</DialogActions>
</Dialog>
</div>
I think its suppose to work that way..

modal not shown in storybook

I have a working modal component which i need to show in storybook as well. It depends on the props
to show and close. For example, if i need to show modal i would have to do the following
const Demo = () => {
const [open, setOpen] = useState(false)
return (
<>
<button onClick={() => setOpen(true)}>Open Modal</button>
<Modal open={open} onClose={() => setOpen(false)}>
<Modal.Header>Modal Header goes here</Modal.Header>
<Modal.Content>Modal Content goes here</Modal.Content>
<Modal.Footer>Modal Footer goes here</Modal.Footer>
</Modal>
</>
)
}
I am using ReactDOM.createPortal to render modal so for this i have created a div element with an id
modal and inside it another div element is created where the modal appears. For such i have created a
story of modal as follow
storiesOf('Modals', module)
.addDecorator(storyFn => (
<>
<div id="modal">
<div>{storyFn()}</div>
</div>
</>
))
.addDecorator(withKnobs)
.add('Modals - Basic', () => (
<>
<button type="button">Show Modal</button>
<Modal open={boolean('open', false)}>
<Modal.Header>Modal Header goes here</Modal.Header>
<Modal.Content>Modal Content goes here</Modal.Content>
<Modal.Footer>Modal Footer goes here</Modal.Footer>
</Modal>
</>
))
My modal is created in the following way
const el = document.createElement('div')
const modal = document.getElementById('modal')
return ReactDOM.createPortal(
<>
<Backdrop onClick={e => closeModal(e)}>
<ModalBox>
<Wrapper position={position} {...props}>
{children}
<CloseIcon icon={faTimesCircle} />
</Wrapper>
</ModalBox>
</Backdrop>
</>,
el,
)

Categories