写在前面(说几句废话)
ant design是一套非常优秀的组件库,其中有react版本和vue版本,二者除了语法 上的差异,使用思想基本大同小异,其中ant design 的Form
组件又是使用频率非常高的组件,但往往,我们在做一些复杂功能的时候,通常需要对其进行二次封装,于是我就遇到了这样的问题
“React和Vue都是作为单向数据流的框架,我们通常是使用‘父传子,子调父’的形式来做组件通信的,对我封装的表单组件,怎么才能将子组件的值暴露在父组件中供父组件的Form
方法捕获,并同意操作值或者校验呢??”
这个问题困扰了我很久,以至于很长一段时间我都是通过回调函数的形式来和父组件通信的,即,父组件传一个操作Form的方法到子组件,子组件在完成特定的事件后,调用这个方法。但这样用起来,我们还得多传一个回调函数,并且在表单统一校验和取值方面也不行,但是没办法,于是,他像一根刺一样埋在了我心里,我总感觉不能和原组件使用方法相同的封装用起来很闹心。
一个偶然的机会,我发现,原来我没有深刻体会到官方文档中的这句话
当年的我还是一个小白,面对复杂的开发,我对这句话的认识只是固定在了现有组件的上面,包括他的
value
,defaultevalue
等,而忽略了他对于自定义组件封装的重要作用,真是这句话,真正解决了子组件像父组件回传值的问题。我们不再使用callback的形式了!好,下面开始进入正题。为尽可能解决大家遇到的问题,这边文章,我将分别使用React和Vue来实现一些功能,供大家参考
Antd中表单封装回传值至父组件
本文不管是antd4.x还是3.x还是ant design vue都适用,这里我使用的是react版本的3.x。
对于上面官网的那句话,现在应该有一个更明确的理解,要想我们的组件可以搭配Form组件使用,并和原生组件用法相同,我们的组件需要遵循以下几点
- 提供受控属性
value
或其它与valuePropName
的值同名的属性。- 提供
onChange
事件或trigger
的值同名的事件- 支持 ref:
- React@16.3.0 之前只有 Class 组件支持。
- React@16.3.0 及之后可以通过forwardRef添加 ref 支持
- Vue中始终都支持ref
下面我们来实践以下如何遵循上面的几条规定
自定义Input组件(React版本)
这里先用react版来做第一个实验,我将组件做了下面的封装和使用
父组件中
import React from 'react'
import CustomInput from './components/CustomInput '
const FormItem = Form.Item;
const Parent= props => {
const { getFieldDecorator } = props.form
const getValue = () => {
props.form.validateFields((err, val) => {
if(!err) {
console.log(val)
}
})
}
const setValue = () => {
props.form.setFieldsValue({title: '909090'})
}
return (
<>
<Button onClick={getValue}>获取表单</Button>
<Button onClick={setValue}>给表单赋值</Button>
<Form>
<FormItem label="标题">
{getFieldDecorator('title', {
})(<CustomInput />)}
</FormItem>
</Form>
</>
)
}
export default Form.create()(Parent)
父组件就是一个常规的对Antd表单项的使用,主要内容是对自定义表单的取值和校验以及对其设值。下面是子组件封装方法
import React from 'react'
import { Input } from 'antd'
const PriceInput = (props, ref) => {
const inputChange = e => {
const { onChange } = props
onChange(e.target.value)
}
return (<Input ref={ref} value={props.value} onChange={inputChange}/>)
}
export default React.forwardRef(PriceInput)
我们没有做太复杂的封装,主要目的是为了演示如何遵循正确的规则去配合Form
组件的使用。上面的组件有以下几点特点
有value属性受控
提供 onChange 事件,这里是最重要的一点,可以看到代码中有Input组件自带的onChange事件,有从props中取的onChange事件,那么规则中的onChange事件指的是哪一个呢?答案是props中的onChange,这一步很管家
支持ref,因为class组件本身支持ref这里我特意用函数组件做实例,看其如何用
这次我们再来对上面那所谓的规律做总结
通过getFieldDecorator
包装后,组件自带value属性,或者指定的valuePropName属性,我们可以通过这个特点 来设置组件的值,达到父组件驱动父组件的目的
通过getFieldDecorator
包装后,组件自带onChange属性,我们可以通过调用这个方法,将值回传给父组件这是相当关键的一步,也是我一直忽略了的一点
自定义Upload组件(Vue版本)
上面我们用react版本的antd演示了如何做正确的做表单项封装,对于需要遵循的规定,也是用了比较常规的value和onChange,下面我们来使用vue版本来演示更复杂的使用方式,思想和上面的简单实例一样。
首先是父组件
<a-form :form="form">
<a-form-item label="自定义上传组件">
<myCom v-decorator="['uploadCom', {trigger: 'uploadDone', valuePropName: 'fileList'}]"/>
</a-form-item>
</a-form>
父组件中,我们设置了trigger
和valuePropName
,即规则中的第一条和第二条。二者都是自定义字段。其中trigger
是我们回传值的时候要调用的方法,valuePropName
是我们要注入到子组件中的属性。
看子组件封装
<template>
<a-upload
name="file"
:multiple="true"
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
:headers="headers"
@change="handleChange"
:fileList="fileList"
>
<a-button> <a-icon type="upload" /> Click to Upload </a-button>
</a-upload>
</template>
<script>
export default {
data () {
return {
headers: {
authorization: 'authorization-text'
}
}
},
props: {
fileList: Array
},
methods: {
handleChange (info) {
this.$listeners.uploadDone(info.fileList)
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList)
}
if (info.file.status === 'done') {
this.$message.success(`${info.file.name} file uploaded successfully`)
} else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} file upload failed.`)
}
}
}
}
</script>
上面为官网完整demo,大家在实际项目中可根据情况封装更复杂的组件,集中上面的代码关键点在以下
props: { fileList: Array // 因为通过v-decorator包装后会自动注入valuePropName属性 }
handleChange (info) { this.$listeners.uploadDone(info.fileList) // 这里我是本地,所以将该方法放在了一开始,可根据需求决定放在哪里,一般是放到文件上传完成后
我们通过this.$listeners.uploadDone
来将值回传,从而保证父组件可以通过Form方法获取值。说实话我也是找了半天才从this.$listeners
里找到uploadDone
,其中uploadDone
是我们在父组件自定义的trigger
,这里为了讲解我故意设置了一下,不设置的话,取默认的onChange
即可。
关于自定义校验
通过上面正确的封装后,我们已经可以通过Form来获取组件暴露的值了,这样我们在做一些自定义检验的时候,就可以使用validator
做自定义校验了
总结
通过上面的封装,我们就可以正常的像用原生antd组件那样使用自定义的组件了,不管你是react用户还是vue用户,思想都是一样,一定要记住上面的三条规范,同时页要反思,文档要深刻理解哦好了,去试试吧本人已亲测有效