I'm currently working on a booking form which is in React using Formik. I've also incorporated Ant Design's Date Picker and Time Picker for the booking date and time respectively, but I'm having difficulties getting the values to be passed back to the component.
Here is how I've set it up in the form component (I've omitted the other unrelated fields):
const { booking, handleSubmit, mode } = this.props;
...
<Formik
initialValues={booking}
onSubmit={handleSubmit}
render={({errors, touched, isSubmitting}) => (
<Form>
...
<div className="form-group col-sm-4 col-md-6 col-lg-4">
<label htmlFor="booking_date">
Booking Date <span className="required">*</span>
</label>
<DatePicker onChange={ (date, dateString) => setFieldValue('booking_date', dateString)} defaultValue={this.state.bookingDate}
className="form-control" format={this.state.dateFormat} />
</div>
<div className="form-group col-sm-4 col-md-6 col-lg-4">
<label htmlFor="start_time">
Start Time <span className="required">*</span>
</label>
<TimePicker
defaultValue={this.state.startTime}
format={this.state.timeFormat}
className="form-control"
onChange={this.handleStartTimeChange}
minuteStep={5}
id="start_time"
name="start_time"
/>
</div>
This is the function that handles the time change (just a state set):
handleStartTimeChange(time) {
this.setState({
startTime: time
});
}
And then on the parent, the component is set up like so:
<BookingForm
show={true}
booking={null}
handleSubmit={this.saveBooking.bind(this)}
mode="add"
/>
And the saveBooking
function simply console logs the params out. However, it only ever logs out the other fields such as firstname
, surname
and email
. The dates are completely overlooked and I don't know how to be able to get the form to recognise them - I even tried creating a Formik hidden field to replicate the date value when submit but it still ignores it. The field name and ID are correct, and correlate with the database as do all the others - so I don't understand why it won't read that data?
Put simply, you'll need to utilize Ant Design's Form.Item
inside of a Formik Field
's component
prop.
You'll be able to add other Antd form items as well, however, there are a few quirks. As such, I'd only recommend using one or the other (not both).
Working example: https://codesandbox.io/s/4x47oznvvx
components/AntFields.js (the reason behind creating two different onChange
functions is because one of the ant components passes back an event
(event.target.value
) while the other passes back a value
-- unfortunately, a quirk when using Formik
with Antd
)
import map from "lodash/map";
import React from "react";
import { DatePicker, Form, Input, TimePicker, Select } from "antd";
const FormItem = Form.Item;
const { Option } = Select;
const CreateAntField = Component => ({
field,
form,
hasFeedback,
label,
selectOptions,
submitCount,
type,
...props
}) => {
const touched = form.touched[field.name];
const submitted = submitCount > 0;
const hasError = form.errors[field.name];
const submittedError = hasError && submitted;
const touchedError = hasError && touched;
const onInputChange = ({ target: { value } }) =>
form.setFieldValue(field.name, value);
const onChange = value => form.setFieldValue(field.name, value);
const onBlur = () => form.setFieldTouched(field.name, true);
return (
<div className="field-container">
<FormItem
label={label}
hasFeedback={
(hasFeedback && submitted) || (hasFeedback && touched) ? true : false
}
help={submittedError || touchedError ? hasError : false}
validateStatus={submittedError || touchedError ? "error" : "success"}
>
<Component
{...field}
{...props}
onBlur={onBlur}
onChange={type ? onInputChange : onChange}
>
{selectOptions &&
map(selectOptions, name => <Option key={name}>{name}</Option>)}
</Component>
</FormItem>
</div>
);
};
export const AntSelect = CreateAntField(Select);
export const AntDatePicker = CreateAntField(DatePicker);
export const AntInput = CreateAntField(Input);
export const AntTimePicker = CreateAntField(TimePicker);
components/FieldFormats.js
export const dateFormat = "MM-DD-YYYY";
export const timeFormat = "HH:mm";
components/ValidateFields.js
import moment from "moment";
import { dateFormat } from "./FieldFormats";
export const validateDate = value => {
let errors;
if (!value) {
errors = "Required!";
} else if (
moment(value).format(dateFormat) < moment(Date.now()).format(dateFormat)
) {
errors = "Invalid date!";
}
return errors;
};
export const validateEmail = value => {
let errors;
if (!value) {
errors = "Required!";
} else if (!/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(value)) {
errors = "Invalid email address!";
}
return errors;
};
export const isRequired = value => (!value ? "Required!" : "");
components/RenderBookingForm.js
import React from "react";
import { Form, Field } from "formik";
import { AntDatePicker, AntInput, AntSelect, AntTimePicker } from "./AntFields";
import { dateFormat, timeFormat } from "./FieldFormats";
import { validateDate, validateEmail, isRequired } from "./ValidateFields";
export default ({ handleSubmit, values, submitCount }) => (
<Form className="form-container" onSubmit={handleSubmit}>
<Field
component={AntInput}
name="email"
type="email"
label="Email"
validate={validateEmail}
submitCount={submitCount}
hasFeedback
/>
<Field
component={AntDatePicker}
name="bookingDate"
label="Booking Date"
defaultValue={values.bookingDate}
format={dateFormat}
validate={validateDate}
submitCount={submitCount}
hasFeedback
/>
<Field
component={AntTimePicker}
name="bookingTime"
label="Booking Time"
defaultValue={values.bookingTime}
format={timeFormat}
hourStep={1}
minuteStep={5}
validate={isRequired}
submitCount={submitCount}
hasFeedback
/>
<Field
component={AntSelect}
name="bookingClient"
label="Client"
defaultValue={values.bookingClient}
selectOptions={values.selectOptions}
validate={isRequired}
submitCount={submitCount}
tokenSeparators={[","]}
style={{ width: 200 }}
hasFeedback
/>
<div className="submit-container">
<button className="ant-btn ant-btn-primary" type="submit">
Submit
</button>
</div>
</Form>
);
components/BookingForm.js
import React, { PureComponent } from "react";
import { Formik } from "formik";
import RenderBookingForm from "./RenderBookingForm";
import { dateFormat, timeFormat } from "./FieldFormats";
import moment from "moment";
const initialValues = {
bookingClient: "",
bookingDate: moment(Date.now()),
bookingTime: moment(Date.now()),
selectOptions: ["Mark", "Bob", "Anthony"]
};
const handleSubmit = formProps => {
const { bookingClient, bookingDate, bookingTime, email } = formProps;
const selectedDate = moment(bookingDate).format(dateFormat);
const selectedTime = moment(bookingTime).format(timeFormat);
alert(
`Email: ${email} \nSelected Date: ${selectedDate} \nSelected Time: ${selectedTime}\nSelected Client: ${bookingClient}`
);
};
export default () => (
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
render={RenderBookingForm}
/>
);