ReduxForm inside Modal - reactjs

I use ReactJS and want to add reduxForm to modal.
For modal I use reactstrap library that represents bootstrap 4 for React.
For form validation I use reduxForm
I have tasked add form modal with validation fields and I did that:
render() {
const {handleSubmit, fields: {
email
}} = this.props;
const FormEmail = ({touched, error}) => {
if (touched && error)
return (
<div>
<Input type="email" name="email" id="email"/>
<div className="error">{error}</div>
</div>
);
return (<Input type="email" name="email" id="email"/>);
}
return (
<div>
<Col>
<Col xs={{
size: 9
}}>
<ReportSettings/>
</Col>
<Col xs={{
size: 3
}}>
<Button outline color="primary" onClick={this.toggle}>Share</Button>
</Col>
</Col>
<Form onSubmit={handleSubmit(this.toggle)}>
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
<ModalHeader toggle={this.toggle}>Share</ModalHeader>
<ModalBody>
<FormGroup row>
<CheckBoxInputField data={this.state.measurements} handleFieldChange={this.handleFieldChange}/>
</FormGroup>
<FormGroup row>
<Label for="email" xs={2}>Email</Label>
<Col xs={10}>
<FormEmail {...email}/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="message" xs={2}>Message</Label>
<Col xs={10}>
<Input type="textarea" name="message" id="message"/>
</Col>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button action="submit" color="primary" value={true}>OK</Button>
<Button color="secondary" onClick={this.toggle} value={false}>Cancel</Button>
</ModalFooter>
</Modal>
</Form>
</div>
);
}
my toggle function:
toggle(e) {
e.preventDefault();
this.setState({
modal: !this.state.modal
});
if (e.target.value === 'true') {
const measurementsID = this.state.measurements.values.filter((measurement) => {
return measurement.checked;
}).map((measurement) => {
return measurement.value;
});
this.props.onShare(MeasurementsToBitMap(measurementsID));
}
}
validation function:
function validate(formProps) {
const errors = {};
if (!formProps.email) {
errors.email = 'Please enter an email';
}
return errors;
}
finally I export Component:
export default reduxForm({form: 'form', fields: ['email'], validate})(HeadMeasurement);
When I click on cancel button the modal close it works good, but when I click on OK button
and fields aren't valid error messages aren't showed otherwise when fields are valid modal isn't disappeared.
My question is how to combine reduxForm and Modal work together?
Thanks, Michael.

reactstrap inserts your modal definition at the bottom of the rendered html doc so wrapping <Form> around <Modal> doesn't work. You have to have your inside the . In your case since you are doing a submit action you will want it wrapped around both your footer and the body tags.
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
<Form onSubmit={handleSubmit(this.toggle)}>
<ModalHeader toggle={this.toggle}>Share</ModalHeader>
<ModalBody>
<FormGroup row>
<CheckBoxInputField data={this.state.measurements} handleFieldChange={this.handleFieldChange}/>
</FormGroup>
<FormGroup row>
<Label for="email" xs={2}>Email</Label>
<Col xs={10}>
<FormEmail {...email}/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="message" xs={2}>Message</Label>
<Col xs={10}>
<Input type="textarea" name="message" id="message"/>
</Col>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button action="submit" color="primary" value={true}>OK</Button>
<Button color="secondary" onClick={this.toggle} value={false}>Cancel</Button>
</ModalFooter>
</Form>
</Modal>
Also, did you bind your toggle to this in your constructor?
constructor(props) {
this.toggle = this.toggle.bind(this);
}
Alternatively, use arrow functions.
toggle = (e) => {
e.preventDefault();
this.setState({
modal: !this.state.modal
});
...
}

Related

why does handleSubmit in react hook useform is not being called

I am using useform hook but the handlesubmit function is not being called . here is the code:
This is the useform hook i am using
const {
register,
handleSubmit,
formState: { errors },
watch,
reset, } = useForm<SellingInvoiceClientDetails>({
resolver: yupResolver(SellingInvoiceScheme),
defaultValues: {
rib: "",
cardNumber: "",
cardType: CardType.IDENTITY_CARD,},});
The function i want to call in the hundleSubmit is the following
const addSellingInvoiceClientDetails = (
sellingInvoiceDetails: SellingInvoiceClientDetails
) => {
console.log(sellingInvoiceDetails.cardType);
props.setSelectedClient();
props.updateSellingInvoiceInfo(
sellingInvoiceDetails.cardType,
sellingInvoiceDetails.cardNumber,
sellingInvoiceDetails.rib
);
handleClose(); };
The code of the Form :
return (
<>
<Modal.Header closeButton>
<Modal.Title>
<FormattedMessage id={"client.info"} />
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form onSubmit={handleSubmit(addSellingInvoiceClientDetails)}>
<Form.Group className="mb-3">
<Form.Label>
<FormattedMessage id={"card.number"} />
</Form.Label>
<Form.Control
{...register("cardNumber")}
placeholder={intl.formatMessage({ id: "card.number" })}
/>
<Form.Text className=" text-danger">
{errors.cardNumber?.message}
</Form.Text>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>
<FormattedMessage id={"card.type"} />
</Form.Label>
<Form.Check
{...register("cardType")}
type={"radio"}
label={intl.formatMessage({ id: CardType.IDENTITY_CARD })}
value={CardType.IDENTITY_CARD}
id={"identity_card"}
/>
<Form.Check
{...register("cardType")}
type={"radio"}
label={intl.formatMessage({ id: CardType.DRIVING_LICENCE })}
value={CardType.DRIVING_LICENCE}
id={"driving_licence"}
/>
<Form.Text className=" text-danger">
{errors.cardType?.message}
</Form.Text>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>RIP</Form.Label>
<input
type="text"
className="form-control"
{...register("rib")}
placeholder="XXXXXXXXXXXXX"
/>
<Form.Text className=" text-danger">
{errors.rib?.message}
</Form.Text>
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
<FormattedMessage id={"cancel"} />
</Button>
<Button
type="submit"
variant="primary"
onClick={handleSubmit(addSellingInvoiceClientDetails)}
>
<FormattedMessage id={"ok"} />
</Button>
</Modal.Footer>
</>
);
the function addSellingInvoiceClientDetails is not being excuted and when i click the Ok button nothing happens altough the handleClose function called in cancel button is working just fine.
You have put the Button element out of the form.
Try to move it inside the <form> tag

Pass form data to modal component React/Boostrap

I have a modal component and form component. I am trying to pass the data using React hooks from the form back to the modal. I am having trouble doing this. Here is what I have so far -
Modal(Parent)
interface ISystem {
systemName: string;
allowNumber: number;
statusCode: string;
createdBy?: string;
createdDate?: string;
}
const ModalForm = (props) => {
const { buttonLabel, className } = props;
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const addButtonHandler = () => {
toggle();
console.log(FORM DATA SHOULD BE HERE)
};
return (
<div>
<Button color="primary" onClick={toggle}>
{buttonLabel}
</Button>
<Modal
isOpen={modal}
toggle={toggle}
className={className}
centered
size="lg"
>
<ModalHeader toggle={toggle}>{buttonLabel}</ModalHeader>
<ModalBody>
<AddSystem></AddSystem>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={addButtonHandler}>
Add
</Button>{" "}
<Button color="secondary" onClick={toggle}>
Cancel
</Button>
</ModalFooter>
</Modal>
</div>
);
};
export default ModalForm;
This is my Form component
const AddSystem = (props) => {
const [systemId, setSystemId] = useState("");
const [systemName, setSystemName] = useState("");
const [allowNumber, setAllowNumber] = useState("");
const [statusCode, setStatusCode] = useState("");
const [lastModifiedBy, setLastModifiedBy] = useState("");
const submitHandler = (event) => {
event.preventDefault();
};
return (
<Fragment>
<Form onSubmit={submitHandler} className="mx-auto">
<Form.Group as={Row} controlId="systemId">
<Form.Label column sm="2">
{" "}
ID{" "}
</Form.Label>
<Col sm="10">
<Form.Control
type="text"
name="systemId"
placeholder="Leave blank for new system"
value={systemId}
disabled
onChange={(event) => setSystemId(event.target.value)}
/>
</Col>
</Form.Group>
<Form.Group as={Row} controlId="systemName">
<Form.Label column sm="2">
{" "}
Systen Name{" "}
</Form.Label>
<Col sm="10">
<Form.Control
type="text"
name="systemName"
placeholder=""
value={systemName}
onChange={(event) => setSystemName(event.target.value)}
/>
</Col>
</Form.Group>
<Form.Group as={Row} controlId="allowNumber">
<Form.Label column sm="2">
{" "}
Allow Number{" "}
</Form.Label>
<Col sm="10">
<Form.Control
as="select"
name="allowNumber"
value={allowNumber}
onSelect={(event) => setAllowNumber(event.target.value)}
>
<option>Choose...</option>
{["1", "2", "3", "4", "5"].map((opt) => (
<option>{opt}</option>
))}
</Form.Control>
</Col>
</Form.Group>
<Form.Group as={Row} controlId="statusCode">
<Form.Label column sm="2">
{" "}
Status Code{" "}
</Form.Label>
<Col sm="10">
<Form.Control
as="select"
name="statusCode"
value={statusCode}
onSelect={(event) => setStatusCode(event.target.value)}
>
<option>Choose...</option>
{["Active", "Draft"].map((opt) => (
<option>{opt}</option>
))}
</Form.Control>
</Col>
</Form.Group>
<Form.Group as={Row} controlId="lastModifiedBy">
<Form.Label column sm="2">
{" "}
Last Modified By{" "}
</Form.Label>
<Col sm="10">
<Form.Control
type="text"
name="lastModifiedBy"
placeholder=""
value={lastModifiedBy}
disabled
onChange={(event) => setLastModifiedBy(event.target.value)}
/>
</Col>
</Form.Group>
</Form>
</Fragment>
);
};
export default AddSystem;
I don't want to have the button inside the form. The button needs to stay in the modal footer but somehow receive information from the form...This is so that the modal can become re-usable and have any type of form passed into it perhaps

How to write condition to Input tag in React

I am working on React project, In that project I have a scenario that is I have to write
Condition for Input tag. The Condition wants to be like this, In my form the Input tag type is
Number, and its min Value is 1001 and max value is 1500. So now what I want is If I type number
Less than 1001 then it should not take that number in Input tag. Someone please help me how to
Write logic like this.
This is Form.js
import React from 'react';
import './aum-company-modal.css';
import { Button, Col, Modal, ModalBody, ModalFooter, ModalHeader, Row, FormGroup, Label, Input, } from 'reactstrap';
const AumCompanyModal = () => {
return (
<Row>
<Col md="6" sm="6" xs="6">
<Modal isOpen
>
<ModalHeader >Add new</ModalHeader>
<ModalBody>
<Row>
<Col md="12" sm="12" xs="12">
<FormGroup>
<Label for="exampleName">Min Value</Label>
<Input
type="text"
name="roleName"
placeholder="Enter minimum value"
value='1000'
/>
</FormGroup>
<FormGroup>
<Label for="exampleName">Max Value</Label>
<Input
type="number"
name="roleName"
placeholder="Enter maximum value"
min='1001' max='1500'
/>
</FormGroup>
</Col>
</Row>
</ModalBody>
<ModalFooter>
<Button color="secondary">
Cancel
</Button>
<Button type="submit" color="primary">
Submit
</Button>
</ModalFooter>
</Modal>
</Col>
</Row>
)
}
export default AumCompanyModal
You input does not have a value property so I would suggest you to make an state to use it as value and the set an onChange which is a function that checks that just like this:
const [ inputValue, setInputValue ] = useState(1000);
handleChange = (e) => {
if (e.target.value > 999 && e.target.value < 1501) {
setInputValue(e.target.value);
}
}
<Input
type="number"
name="roleName"
placeholder="Enter maximum value"
min='1001' max='1500'
onChange={handleChange}
value={inputValue}
/>

How do I reset modal props on close

I have a component, DeleteRoute, which is just a wrapper around a Modal. It allows me to delete a "Route" entity.
I am using React as well as Redux to maintain data and state.
I am struggling with the correct architecture to make the modal work. I didn't like having all of the state in the parent container because it made the parent cluttered. So I put the redux actions inside the modal. This way I pass in a route from the parent, but call "deleteRoute" from the redux store inside the DeleteRoute component, and I can display success and error messages.
This all works great, except it I close and re-open the modal, the previous success/error message is still displayed. This is because the closing/opening is done with a toggle from the parent, but the parent can't reset the child props.
I thought I could just pass the success and error properties in the parent, and anytime the parent re-renders it would reset those, but it isn't (despite it re-rendering when isOpen changes).
class DeleteRoute extends React.Component {
constructor(props) {
super(props);
this.deleteRoute = this.deleteRoute.bind(this);
}
deleteRoute() {
this.props.deleteRoute(this.props.route);
}
render() {
var route = this.props.route || {};
return (
<div>
<Modal
isOpen={this.props.isOpen}
toggle={this.props.toggle}
>
<ModalHeader toggle={this.props.toggle}>Delete Route</ModalHeader>
<ModalBody>
{this.props.isLoading && <Spinner color="primary" />}
<Alert color="danger" isOpen={this.props.error}>{this.props.error}</Alert>
<Alert color="success" isOpen={this.props.success}>Deleted successfully</Alert>
<Form>
<Row form>
<Col>
<FormGroup>
<Label for="CssPlatform">Css Platform</Label>
<Input disabled name="CssPlatform" type="text" value={route.CSSPlatform} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="ProdManager">Prod Manager</Label>
<Input disabled name="ProdManager" type="text" value={route.ProdManager} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup>
<Label for="CssProduct">Css Product</Label>
<Input disabled name="CssProduct" type="text" value={route.CSSProduct} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="ProgSupervisor">Prog Supervisor</Label>
<Input disabled name="ProgSupervisor" type="text" value={route.ProgSupervisor} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup>
<Label for="CssSubProduct">Css SubProduct</Label>
<Input disabled name="CssSubProduct" type="text" value={route.CSSSubProduct} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="RallyProject">Rally Project</Label>
<Input disabled name="RallyProject" type="text" value={route.RallyProject} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup check inline>
<Label check>
<Input disabled name="CssProductActive" type="checkbox" checked={route.CSSProductActive} />
Css Product Active
</Label>
</FormGroup>
</Col>
<Col>
<FormGroup check inline>
<Label check>
<Input disabled name="CssSubProductActive" type="checkbox" checked={route.CSSSubProductActive} />
Css SubProduct Active
</Label>
</FormGroup>
</Col>
</Row>
</Form>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.deleteRoute}>Delete Route</Button>{' '}
<Button color="secondary" onClick={this.props.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default connect(
state => state.deleteRouteReducer,
dispatch => bindActionCreators(actionCreators, dispatch))(DeleteRoute);
render() {
return (
<div>
<h2>Routes</h2>
<p>Routes define how items from CSS get imported into Rally. Use routes to connect a Rally project to a set of criteria in CSS.</p>
<div>
<AddRoute isOpen={this.props.showAddRoute} toggle={this.toggleAddRoute} />
<DeleteRoute error={this.props.deleteRouteError} success={this.props.deleteRouteSuccess} isOpen={this.props.showDeleteRoute} route={this.props.selectedRoute} toggle={this.toggleDeleteRoute} />
<DataTable
actions={[
{ Click: this.toggleAddRoute, Color: 'green', Icon: 'MdAdd', ToolTip: "Add new route" },
{ Click: this.toggleEditRoute, Color: 'orange', Icon: 'MdEdit', ToolTip: "Edit route", RowAction: true },
{ Click: this.toggleDeleteRoute, Color: 'red', Icon: 'MdRemove', ToolTip: "Delete route", RowAction: true },
]}
columns={[
{ Title: "Platform", Field: "CSSPlatform" },
{ Title: "Product", Field: "CSSProduct" },
{ Title: "SubProduct", Field: "CSSSubProduct" },
{ Title: "ProdManager", Field: "ProdManager" },
{ Title: "ProgSupervisor", Field: "ProgSupervisor" },
{ Title: "Product Active?", Field: "CSSProductActive" },
{ Title: "SubProduct Active?", Field: "CSSSubProductActive" },
{ Title: "Rally Project", Field: "RallyProject" },
{ Title: "Rally Url", Field: "RallyUrl" }
]}
data={this.props.routes}
edit={this.editRoute}
isLoading={this.props.isLoading} />
</div>
</div>
);
I ended up adding a redux action to reset the success and error props, and then calling that when the modal is closed. I think this is in line with correct redux architecture, but I'm open to better solutions if anyone has them.
class DeleteRoute extends React.Component {
constructor(props) {
super(props);
this.deleteRoute = this.deleteRoute.bind(this);
this.toggle = this.toggle.bind(this);
}
deleteRoute() {
this.props.deleteRoute(this.props.route);
}
toggle() {
if (this.props.isOpen) {
// reset the error and success messages on close
this.props.initialize();
}
this.props.toggle();
}
render() {
var route = this.props.route || {};
return (
<div>
<Modal
isOpen={this.props.isOpen}
toggle={this.toggle}
>
<ModalHeader toggle={this.toggle}>Delete Route</ModalHeader>
<ModalBody>
{this.props.isLoading && <Spinner color="primary" />}
<Alert color="danger" isOpen={this.props.error}>{this.props.error}</Alert>
<Alert color="success" isOpen={this.props.success}>Deleted successfully</Alert>
<Form>
<Row form>
<Col>
<FormGroup>
<Label for="CssPlatform">Css Platform</Label>
<Input disabled name="CssPlatform" type="text" value={route.CSSPlatform} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="ProdManager">Prod Manager</Label>
<Input disabled name="ProdManager" type="text" value={route.ProdManager} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup>
<Label for="CssProduct">Css Product</Label>
<Input disabled name="CssProduct" type="text" value={route.CSSProduct} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="ProgSupervisor">Prog Supervisor</Label>
<Input disabled name="ProgSupervisor" type="text" value={route.ProgSupervisor} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup>
<Label for="CssSubProduct">Css SubProduct</Label>
<Input disabled name="CssSubProduct" type="text" value={route.CSSSubProduct} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label for="RallyProject">Rally Project</Label>
<Input disabled name="RallyProject" type="text" value={route.RallyProject} />
</FormGroup>
</Col>
</Row>
<Row form>
<Col>
<FormGroup check inline>
<Label check>
<Input disabled name="CssProductActive" type="checkbox" checked={route.CSSProductActive} />
Css Product Active
</Label>
</FormGroup>
</Col>
<Col>
<FormGroup check inline>
<Label check>
<Input disabled name="CssSubProductActive" type="checkbox" checked={route.CSSSubProductActive} />
Css SubProduct Active
</Label>
</FormGroup>
</Col>
</Row>
</Form>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.deleteRoute}>Delete Route</Button>{' '}
<Button color="secondary" onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default connect(
state => state.deleteRouteReducer,
dispatch => bindActionCreators(actionCreators, dispatch))(DeleteRoute);
and the redux bits
import axios from 'axios';
// actions
const deleteRouteType = "DELETE_ROUTE";
const deleteRouteFailureType = "DELETE_ROUTE_FAILURE";
const deleteRouteSuccessType = "DELETE_ROUTE_SUCCESS";
const initializeType = "DELETE_ROUTE_INITIALIZE";
const initialState = { error: null, success: null };
// action creators
export const actionCreators = {
initialize: () => (dispatch) => {
dispatch({ type: initializeType });
},
deleteRoute: (route) => async (dispatch) => {
dispatch({ type: deleteRouteType });
axios
.delete(`api/route`, route)
.then(res => {
if (res.data.error) {
dispatch({ type: deleteRouteFailureType, payload: res.data.errorMessage });
}
else {
dispatch({ type: deleteRouteSuccessType, payload: res.data.data });
}
})
.catch(err => {
dispatch({ type: deleteRouteFailureType, payload: err.message });
});
}
};
// reducers
export const reducer = (state, action) => {
state = state || initialState;
switch (action.type) {
case initializeType:
return {
...state,
error: null,
isLoading: false,
success: false
};
case deleteRouteType:
return {
...state,
error: null,
isLoading: true,
success: false
};
case deleteRouteFailureType:
return {
...state,
error: action.payload,
isLoading: false,
success: false
};
case deleteRouteSuccessType:
return {
...state,
error: null,
isLoading: false,
success: true
};
default:
return state;
}
};

react+redux with react-bootstrap > FormControl > get value

I am using react+redux with react-bootstrap components.
I would like to pass the value of a FormControl text element (email) to the dispatched redux action but I do not know how to do that.
class LoginForm extends React.Component {
render() {
const email = React.findDOMNode(this.refs.email);
return (
<div>
<Form horizontal>
<FormGroup controlId="formHorizontalEmail">
<Col componentClass={ControlLabel}>Email</Col>
<Col><FormControl type="email" ref="email"/></Col>
</FormGroup>
<FormGroup>
<Col>
<Button type="submit" block>Sign in</Button>
</Col>
</FormGroup>
</Form>
<Button onClick={() => this.props.doLogin(email, 'password')}>Login</Button>
</div>
)
}
}
/**
* Connect staff.
*/
const mapStateToProps = (state) => {
return {
...
};
};
const mapDispatchToProps = (dispatch) => {
return {
doLogin: (email, password) => dispatch(performLogin(email, password))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(LoginForm)
The only one way how to read text value of a FormControl with React (according to my research) is this:
class LoginForm extends React.Component {
handleOnChange = (event) => {
this.setState({ [event.target.id]: event.target.value }, null);
}
render() {
return (
<div>
<Form horizontal>
<FormGroup controlId="email">
<Col componentClass={ControlLabel}}>Email</Col>
<Col>
<FormControl type="email" placeholder="Email" onChange={this.handleOnChange}
/>
</Col>
</FormGroup>
<FormGroup controlId="password">
<Col componentClass={ControlLabel} sm={2}>Password</Col>
<Col>
<FormControl type="password" placeholder="Password" onChange={this.handleOnChange} />
</Col>
</FormGroup>
<FormGroup>
<Col>
<Button onClick={() => this.props.doLogin(this.state.email, this.state.password)}>Submit</Button>
</Col>
</FormGroup>
</Form>
</div>
)
}
}

Resources