最近在折腾
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
的是怎么获取到控件的值的呢?
从上图的react devtools
中,我们可以看到,经过getFieldDecorator包装,ant
往DatePicker
控件上注入的相关属性,其中包括了几个事件监听方法(灰色框框部分),其中onChange嫌疑最大,先拿它开刀。
先来看看开刀前的效果,上面代码console.log('values--', values)
打印结果如下:
发起请求后,数据如下:
步骤三:验证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"。结果如下图:
上面的图片说明我们的猜测是正确的,但是这只是成功了一半,我们还需要让moment
对象传给axios
后,在提交的时候自动把自己转成long
型。
步骤四:理清moment经axios后是如何转换的
从上面的请求截图可以看出,moment
对象最终会被转换成UTC时间格式的数据。而且我们也可以很清晰的猜出,这个过程是在axios
中进行的。
关键是怎么转的,因为这是两个完全解耦的第三方库,所以不可能是通过调用moment
特有的方法实现的,应该是Object
上的通用方法。具体是什么呢,toString
?valueOf
?还是其他的呢?
如何验证呢。。。
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哈哈~