Ant 设计日期和时间选择器不通过 Formik 传递值(反应)

Posted

技术标签:

【中文标题】Ant 设计日期和时间选择器不通过 Formik 传递值(反应)【英文标题】:Ant design date and time pickers do not pass value through Formik (react) 【发布时间】:2019-04-20 15:47:40 【问题描述】:

我目前正在使用 Formik 在 React 中处理预订表单。我还分别为预订日期和时间合并了 Ant Design 的 Date Picker 和 Time Picker,但是我很难将值传递回组件。

这是我在表单组件中的设置方式(我省略了其他不相关的字段):

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>

这是处理时间变化的函数(只是一个状态集):

handleStartTimeChange(time) 
    this.setState(
        startTime: time
    );

然后在父节点上,组件是这样设置的:

<BookingForm
    show=true
    booking=null
    handleSubmit=this.saveBooking.bind(this)
    mode="add"
/>

saveBooking 函数只是简单地控制台记录参数。但是,它只会注销其他字段,例如 firstnamesurnameemail。日期完全被忽略了,我不知道如何让表单识别它们——我什至尝试创建一个 Formik 隐藏字段来在提交时复制日期值,但它仍然忽略它。字段名称和 ID 是正确的,并且与所有其他字段一样与数据库相关 - 所以我不明白为什么它不会读取该数据?

【问题讨论】:

【参考方案1】:

简单地说,您需要在 Formik Fieldcomponent 属性中使用 Ant Design 的 Form.Item

您也可以添加其他 Antd 表单项,但是有一些怪癖。因此,我只建议使用其中一种(不要同时使用)。

工作示例:https://codesandbox.io/s/4x47oznvvx

components/AntFields.js(创建两个不同的onChange 函数的原因是因为其中一个ant 组件传回event (event.target.value) 而另一个传回一个value -- 不幸的是,将FormikAntd 一起使用时的怪癖)

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+)*(\.\w2,3)+$/.test(value)) 
    errors = "Invalid email address!";
  

  return errors;
;

export const isRequired = value => (!value ? "Required!" : "");

组件/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
  />
);

【讨论】:

我知道这已在不久前得到回答并且确实有效 - 但我有一个问题。我需要将数据传递给呈现的表单(即从选择框中选择的客户端列表),但由于 RenderBookingForm 只是一个导出的常量,我如何将这些数据作为道具传递,然后在RenderBookingForm 组件?这可能吗? 请参阅上面的更新答案(codesandbox 也已更新)。请注意,您必须想出一个策略来创建selectOptions。无论是通过使BookingForm 成为stateful component,它在从API 的componentDidMount 方法中获取客户端列表时,有条件地呈现(reactjs.org/docs/conditional-rendering.html)一个微调器。然后将此 API 客户端列表设置为 state,然后将其传递给表单。或者,通过将selectOptions 设置为字符串的静态数组(如上所示)。无论哪种方式,它的结构都需要:[ "opt1", "opt2" ... ] 啊,我没想到将它传递给 initialValues!是的,我将从 API 调用中检索客户端,所以我只需要转换为数组而不是我通常会得到的对象,谢谢!【参考方案2】:

我不明白为什么它不会读取该数据?

Formik 将值作为values 属性传递,它们使用setFieldValue 更新。 当您在状态中存储值时,Formik 对此一无所知

当然,将值存储到状态并没有错(假设它有效),但是您必须定义内部提交处理程序才能将这些值附加到其他状态。通过简单的调用 prop:

onSubmit=handleSubmit

你没有机会这样做。只有 Formik 处理的值会被传递。您需要定义内部提交处理程序,例如:

const handleSubmit = values => 
  // init with other Formik fields
  let preparedValues =  ...values ; 

  // values from state
  const  startTime, startDate  = this.state; 

  // attach directly or format with moment
  preparedValues["startTime"] = startTime;
  preparedValues["startDate"] = startDate;

  // of course w/o formatting it can be done shorter
  // let preparedValues =  ...values, ...this.state ; 

  console.log(preparedValues);

  // call external handler with all values
  this.prop.handleSubmit( preparedValues );

【讨论】:

以上是关于Ant 设计日期和时间选择器不通过 Formik 传递值(反应)的主要内容,如果未能解决你的问题,请参考以下文章

日期选择器不显示

为啥这个引导日期选择器不显示日期选择器?

带有自定义组件的 Material ui 文本字段和 Formik 表单

ajax调用后div中的日期选择器不起作用

Ionic 3 日期时间选择器不显示日期

Django Forms - 当字段具有初始数据时,JQuery 日期时间选择器不显示