[RESOLVED]How to cancel "bootstrap modal" exit behavior? - javascript

I need to validate data (eg. fill up all of the fields in the form) before users intend to close the modal via close-button or backdrop-area.
If it's valid then let the exit process going otherwise cancel it.
<Modal show={isShow} keyboard={false} >
<Modal.Header>
<Modal.Title>Profile</Modal.Title>
<CloseButton onClick={onClose} />
</Modal.Header>
<Modal.Body>
{...}
</Modal.Body>
</Modal>
It's possible to do this in React?
Solution
According to Modal#onHide description, I listen to the onHide then validate data before close it.
const onModalHide = () => {
if (validateData()) setIsShow(false);
};
<Modal show={isShow} onHide={onModalHide} >
<Modal.Header>
<Modal.Title>Profile</Modal.Title>
</Modal.Header>
<Modal.Body>
{...}
</Modal.Body>
</Modal>

You will need to manually select the modal id and listen on hide.bs.modal event
You are using bootstrap so probably already import jquery so:
$('#myModal').on('hide.bs.modal', function (e) {
if (!validated) return e.preventDefault()
})
Or with js:
document.addEventListener('hide.bs.modal', function(e) {
if (event.target.id == 'myModal') {
if (!validated) return e.preventDefault();
}
});

I'm guessing onClose is changing the isShow state.
In that case just check whether or not you want to close the modal:
const onClose = () => {
if(validateFormLogic()) //this is where you validate your form
setIsShow(false);
}
<Modal show={isShow} keyboard={false} >
<Modal.Header>
<Modal.Title>Profile</Modal.Title>
<CloseButton onClick={onClose} />
</Modal.Header>
<Modal.Body>
{...}
</Modal.Body>
</Modal>
You decide when to close the modal, not bootstrap nor react.

Related

Mapping to React Modal only renders the last item of the array

My issue is that when I map the data into the Modal, it is only returning the last item in the array. It looks like it is actually opening a modal for every object in the array and stays open on the last object.
I am also using React-Bootstrap-Table-Next in the same component. The button that I want to trigger the Modal with is currently in a column on the table. I would appreciate some input as I feel like I have hit a wall.
Button being rendered in the table with onClick handler to open the modal:
const columns = [
{ dataField: "View", text: "View",
formatter: () => {return <Button variant="success" onClick={handleShow}>View</Button>},
headerStyle: () => {return { width: "5%" };} },
{ dataField: "BusinessName", text: "Business Name", headerStyle: () => {return { width: "27%" };} },
];
Modal state handling:
const [showModal, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
Modal code with map function:
<div>
{submitted.map((submitted) => (
<Modal
show={showModal}
onHide={handleClose}
size="lg"
key={submitted.Id}
centered
>
<Modal.Header closeButton>
<Modal.Title>{submitted.BusinessName}</Modal.Title>
</Modal.Header>
<Modal.Body>{submitted.Email}</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
))}
</div>
Getting and setting the data:
const [submitted, setSubmitted] = useState([]);
const getSubmittedData = async () => {
try {
const response = await Axios.get(url);
setSubmitted(response.data);
console.log(response.data);
setLoading(true);
} catch (e) {
console.log(e)
}
};
I'm not entirely sure what you are trying to do. If you click in a button to show a modal with the information of just one business, you wouldn't need to use map. You just pass the object you need into the Modal and use that:
...
<Modal.Body>
<div>Business Name: {business.BusinessName}</div>
<div>Business Email: {business.Email}</div>
</Modal.Body>
...
business would be one record filtered from all records (eg: by email), something like:
const business = submitted.find(({email}) => email === 'some.email#example.com')
But, if you want to have a list of submitted businesses showing in a modal, you need to only include the repeatable elements in the map.
In this case we have a modal with its different parts (Header, Footer, Body, etc) which should be an individual object being rendered. You then need to map the contents of your submitted list and output what you need.
Something like:
<div>
<Modal show={showModal} onHide={handleClose} size="lg" centered>
<Modal.Header closeButton>
<Modal.Title>Businesses</Modal.Title>
</Modal.Header>
<Modal.Body>
{submitted.map((submitted) => (
<div key={submitted.Id}>
<div>Business Name: {submitted.BusinessName}</div>
<div>Business Email: {submitted.Email}</div>
</div>
))}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>

React Bootstrap Modal not working on submit Button

I am looking to send the value from the Form.Control to the button so I can do some logic but I cannot seem to get this value from the input. I tried using reft but I cant use refs in a functional component, so I'm using the textInput. Please help, been stuck for 3 days. thx
import React from "react";
import { Modal, Form, Row, Col, Button } from "react-bootstrap";
import "./Login.css";
export const ForgotPassword = (props) => {
let textInput = null;
const handleClick = () => {
console.log("i am here", textInput.focus());
};
return (
<>
<Modal
{...props}
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Forgot Password
</Modal.Title>
</Modal.Header>
<Modal.Body className="show-grid">
<Form.Label>Please enter your Employee ID.</Form.Label>
<Row>
<Col className="lm-qus" md={5}>
Employee ID:
</Col>
<Col md={7}>
<Form.Control
type="text"
required
name="answer"
placeholder=""
ref={(input) => {
textInput = input;
}}
/>
</Col>
</Row>
</Modal.Body>
<Modal.Footer>
<Button onClick={props.onHide}>Cancel</Button>
<Button onClick={handleClick} type="submit">
Continue
</Button>
</Modal.Footer>
</Modal>
</>
);
};
This is not a modal specific problem. Make your Form.Control a controlled input by providing "value" attribute. Define state and set this state on change event of the input. You can then access this state everywhere, including the button:
const [value, setValue] = React.useState("");
const handleChange = (event) => setValue(event.target.value);
const handleClick = () => alert(value);
https://codesandbox.io/s/objective-pond-rs8r9f

how to show react bootstrap modal inside the function

I am using react bootstrap modal popup. when user submit form, I need to modal show popup,
This is my modal (SaveConfirmationModal )
import { Button, Modal} from "react-bootstrap";
function SaveConfirmationModal(props) {
return (
<div>
<Modal {...props} size="lg">
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
do u want to save ?
</Modal.Title>
</Modal.Header>
<Modal.Body>
<button>YEs </button>
<button>No</button>
</Modal.Body>
</Modal>
</div>
);
}
export default SaveConfirmationModal;
this is my invoice page save function. I imported my modal in to the invoice page.
import SaveConfirmationModal from "components/createinvoice/SaveConfirmationModal";
const loadPopup= (data) => {
showmodal;
if(yes){
saveForm();
}
else{
close modal
}
}
const saveForm= (data){
my save function
}
my save button in the invoice page
<button onClick={loadPopup}> Save </button>
This is the very little sample to demonstrate my issue. If you can please help me to show this confirmation box. thanks
In the component where you use your SaveConfirmationModal, you can use state Hooks, like this:
import React, {useState} from 'react';
...
const [showModal, setShowModal] = useState(false);
const loadPopup = () => {
setShowModal(true);
};
And you need to change the state to false when the modal is dismissed:
<SaveConfirmationModal show={showModal} onHide={() => setShowModal(false)}
You can call a function from a prop on button click like below.
import { Button, Modal} from "react-bootstrap";
function SaveConfirmationModal(props) {
return (
<div>
<Modal {...props} size="lg">
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
do u want to save ?
</Modal.Title>
</Modal.Header>
<Modal.Body>
<button onClick={props.onYes}>YEs </button>
<button onClick={props.onNo}>No</button>
</Modal.Body>
</Modal>
</div>
);
}
export default SaveConfirmationModal;
Then in your invoice page create a state for showing a modal and pass the props like below.
import { useState } from 'react'
import SaveConfirmationModal from "components/createinvoice/SaveConfirmationModal";
const InvoicePage = () => {
const [showModal, setShowModal] = useState(false);
const saveForm = (data) {
// my save function
}
return (
<>
<button onClick={() => setShowModal(true)}> Save </button>
<SaveConfirmationModal show={showModal} onYes={() => saveForm(data)} onNow={() => setShowModal(false)} />
</>
)
}

TypeScript React Invalid Hook Call Error ("Error: Invalid hook call.")

I tried asking a question earlier but realized it was a bit all over the place so no one could really understand it properly. So here's a more clarified attempt.
I am currently working on a CRUD web project that allows the user to edit, view and create more users which is then displayed on a table.
I have been using react-bootstrap's components so decided to use the Modal component provided. (https://react-bootstrap.netlify.com/components/modal/#modals-live)
It was successfully working without any errors when I just called and used the component within App.tsx as follows:
const App: React.FC = () => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div className="App">
<NavBar/>
<Jumbotron fluid className="userJumbotron">
<Container>
<h1>Manage Users</h1>
<Button variant="outline-dark" onClick={handleShow}>Add new user</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add User</Modal.Title>
</Modal.Header>
<Modal.Body><NewUserForm/></Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
But I think realized that I'll need more than 1 modal component for my program and decided to make the modal component section into my own component that I can reuse as many times as I want and customize as I want within the App.tsx
So I decided to make a NewModal.tsx component that has the bootstrap modal component and button pre-made meaning I'd only have to call one line () rather than a whole chunk. the NewModal.tsx code is as follows:
export default class NewModal extends Component<any> {
constructor (props: any){
super(props)
this.state={
show:false
}
}
render() {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div>
<h1>Manage Users</h1>
<Button variant="outline-dark" onClick={handleShow}>Add new user</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add User</Modal.Title>
</Modal.Header>
<Modal.Body><NewUserForm/></Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
I am getting the following error from this code.
What is the cause for this error?
You can't use hooks inside class components, you need to change it to be a function based component, which can look something like this:
const NewModal = () => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div>
<h1>Manage Users</h1>
<Button variant="outline-dark" onClick={handleShow}>
Add new user
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add User</Modal.Title>
</Modal.Header>
<Modal.Body>
<NewUserForm />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
export default NewModal;

Can successfully open modal when I need to but cannot close it

I am trying to use React boostrap modal, I can successfully open it when i need to but I cannot close it. I have no idea what i am doing wrong.
my markup
``` <Modal show={this.props.show} >
<Modal.Header>
<Modal.Title>Enter Log</Modal.Title>
</Modal.Header>
<Modal.Body>
Hello i am Modal!!!!!
</Modal.Body>
<Modal.Footer>
<Button onClick={() => {closeModal()}} variant="primary"> Close </Button>
</Modal.Footer>```
state:
constructor(props) {
super(props)
this.state = {
closeModal: false
}
}
Handler function:
const closeModal = () => {
this.setState({closeModal: true});
}
You can do this way,
const closeModal = () => {
this.setState({ show: false});
}
<Modal show={this.props.show} onHide={this.props.closeModal} >
<Modal.Header closeButton>
<Modal.Title>Enter Log</Modal.Title>
</Modal.Header>
<Modal.Body>
<p> Hello i am Modal!!!!! </p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.closeModal} variant="primary"> Close </Button>
</Modal.Footer>
</Modal>
refrence https://react-bootstrap.github.io/components/modal/
You need to use the same condition to check if render the modal or not
You are using props to check if your modal should be rendered and when closing it, you are setting state to false.
If you want to use props you will need to update your props, using redux and dispatching an action or whatever
According to yor handleClose function, you will need to check state instead of props in the condition
const closeModal = () => {
this.setState({closeModal: true});
}
<Modal show={this.props.show} >
<Modal.Header>
<Modal.Title>Enter Log</Modal.Title>
</Modal.Header>
<Modal.Body>
Hello i am Modal!!!!!
</Modal.Body>
<Modal.Footer>
<Button onClick={() => this.closeModal()} variant="primary"> Close </Button>
</Modal.Footer>

Categories