阅读 1124

react->Ant Design->日期控件DatePicker数据提交格式设置

最近在折腾react,所以,将一些自己碰到的问题以及对应的解决办法分享出来,期待和大家共同探讨和进步!

概述

react折腾系列之二:如何在ant框架下,form表单中,灵活设置DatePicker提交服务器的数据格式。

例如:日期展示为:“2018-08-08”,然后提交服务器的时候,提交为:1533657600000(即:new Date('2018-8-8').getTime()的值)。

通过官方文档,我们知道,可以通过format字段设置日期格式,并且其值是一个moment对象。所以你可能会想到:

  • 设置format为"YYYY-MM-DD"
  • 在提交服务器前,通过该日期选择框的moment对象获取到对应的long型数值,然后提交

当然也是ok的,但是这样会比较麻烦,毕竟每个相关的ajax请求,都需要加上这样的逻辑。

本着,拒绝重复工作和自己的事情自己做的原则,我们希望组件自身支持这样的功能。

问题描述清楚了,再提一下,本文以axios为例进行讲解,毕竟不一样的库,处理方式可能不一样,但是道理是相通的。

步骤一:看文档

理所当然,首先应该把DatePicker的官方文档啃一遍,看看支不支持该功能。

当然,我已经啃过,就因为没有,才会有本文。

读者可以先自行脑补一下,如果是你,会怎么实现这个功能呢。

步骤二:理清getFieldDecorator与控件的交互逻辑

在进行form表单开发时,我们一般会使用ant官方提供的getFieldDecorator对控件进行包装后再使用,例如:

<Form>
    <Form.Item label="DatePicker">
        {getFieldDecorator('date-picker', config)(<DatePicker />)}
    </Form.Item>
</Form>
复制代码

然后调用validateFields进行校验,并在其回调中进行数据提交,例如:

props.form.validateFields((err, values) => {
    console.log('values--', values)
    if (!err) {
    // do something
    submitForm(values) // 数据提交
    }
})
复制代码

如果,你在上面代码中的do something处,把对应的日期moment对象转换成long型,然后再传给submitForm,这样也是可以解决问题的,但是这样就需要在每个类似的地方书写这样的逻辑,并不是我们想要的。

那么form.validateFields的是怎么获取到控件的值的呢?

图片1

从上图的react devtools中,我们可以看到,经过getFieldDecorator包装,antDatePicker控件上注入的相关属性,其中包括了几个事件监听方法(灰色框框部分),其中onChange嫌疑最大,先拿它开刀。

先来看看开刀前的效果,上面代码console.log('values--', values)打印结果如下:

图片2

发起请求后,数据如下:

图片3

步骤三:验证getFieldDecorator与onChange的关系

接下来,我们验证一下,getFieldDecorator是不是通过onChange来实现控件值的获取的。

import React from 'react'
import { DatePicker } from 'antd'
import moment from 'moment'

const { RangePicker } = DatePicker

function MyDatePicker (props) {
  var oldChange = props.onChange
  var newProps = {
    ...props, 
    onChange: function (date, dateStrings) {
      date[0] = moment("2018-08-08")
      date[1] = moment("2019-09-09")
      oldChange(date, dateStrings)
    }
  }
  return <RangePicker {...newProps}/>
}

export default MyDatePicker
复制代码

如上面的代码所示,如果我们的猜测是对的,那么无论你选择的时间区间是什么时候,打印的结果应该都是"2018-08-08"到"2019-09-09"。结果如下图:

图片3
图片4

上面的图片说明我们的猜测是正确的,但是这只是成功了一半,我们还需要让moment对象传给axios后,在提交的时候自动把自己转成long型。

步骤四:理清moment经axios后是如何转换的

从上面的请求截图可以看出,moment对象最终会被转换成UTC时间格式的数据。而且我们也可以很清晰的猜出,这个过程是在axios中进行的。

关键是怎么转的,因为这是两个完全解耦的第三方库,所以不可能是通过调用moment特有的方法实现的,应该是Object上的通用方法。具体是什么呢,toStringvalueOf?还是其他的呢?

如何验证呢。。。

date[0] = moment("2018-08-08")
date[0].toString = function () {
    return '123'
}
复制代码

如果发起请求的时候,数据变成了"123",那就说明猜测正确了。

当然,笔者已经帮你们试过了,不是toString,也不是valueOf。如果你猜中了,那么恭喜恭喜;如果你没猜中,而且再也想不出来还有哪些方法存在可能性。那咋办。

要么,瞄一瞄服务端开发同学鄙视的眼神(这种小问题都搞不定?),然后你就有动力继续猜了,O(∩_∩)O哈哈~

要么就只能放大招了,看看axios的源码了。

// defaults.js中
transformRequest: [function transformRequest(data, headers) {
    // 此处省略n行代码
    if (utils.isArrayBufferView(data)) {
      return data.buffer;
    }
    if (utils.isURLSearchParams(data)) {
      setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
      return data.toString();
    }
    if (utils.isObject(data)) {
      setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
      return JSON.stringify(data);
    }
    return data;
  }],
复制代码

看出来了吧,原来是通过JSON.stringify(data),那么,接下来就好办了。

还不知道怎么办?

看看我鄙视的小眼神,→_→

重写toJSON就行了,(~ ̄▽ ̄)~

终极代码

import React from 'react'
import { DatePicker } from 'antd'

const { RangePicker } = DatePicker;

function MyDatePicker (props) {
  var serverFormat = props.serverFormat
  var oldChange = props.onChange
  var newProps = {
    ...props, 
    onChange: function (date, dateStrings) {
      transformMoment(date[0], serverFormat)
      transformMoment(date[0], serverFormat)
      oldChange(date, dateStrings)
    }
  }
  return <RangePicker {...newProps}/>
}

function transformMoment (myMoment, serverFormat) {
  myMoment.toJSON = function () {
    serverFormat = serverFormat || 'x'
    var value = myMoment.format(serverFormat)
    if (serverFormat === 'x') {
      value = parseInt(value)
    }
    return value
  }
  return myMoment
}

export default MyDatePicker
复制代码

这样,就解决了!结果就不截图了,相信讲到这一步,你自己就能搞定了。而且还可以通过serverFormat属性,修改提交给服务端的数据格式。

以后你使用起来,就是:

// 提交格式为long型
<Form>
    <Form.Item label="DatePicker">
        {getFieldDecorator('date-picker', config)(<MyDatePicker />)}
    </Form.Item>
</Form>

// 提交格式为"YYYY-MM-DD",例如:2018-08-08
<Form>
    <Form.Item label="DatePicker">
        {getFieldDecorator('date-picker', config)(<MyDatePicker serverFormat="YYYY-MM-DD" />)}
    </Form.Item>
</Form>
复制代码

是不是棒棒的,那么请不要吝啬你的赞。好人一生平安,O(∩_∩)O哈哈~