Ant Design
Ant Design是一个内容丰富的组件库,在项目结构越来越复杂时,一个组件会用到多个子组件,多个子组件可能都会用到Ant Design的Form表单组件。
getFieldDecorator
在Form表单中,有一个可以对输入框进行限制的Api为getFieldDecorator,,主要作用是用于和表单进行双向绑定,在经过getFieldDecorator包装后,会自动添加value和onChange属性,数据同步将被Form接管。如此,会形成以下规则:
1、不需要用 onChange 来做同步,但还可以继续监听 onChange 等事件;
2、不能用控件的 value defaultValue 等属性来设置表单域的值,默认值可以用 getFieldDecorator 里的 initialValue;
3、不能用 setState,可以使用 this.props.form.setFieldsValue 来动态改变表单值。
import React, { Component } from 'react';
import {
Form,
Input,
Select,
} from 'antd';
const { Option } = Select;
class AddOrUpdate extends Component {
constructor(props) {
super(props);
this.state = {
currentData:''
}
}
// 点击提交
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
// 点击改变下拉框
handleChange=(value)=>{
console.log(`selected ${value}`);
}
UNSAFE_componentWillMount() {
// 把form对象传递给父组件
this.props.setForm(this.props.form);
}
render() {
const { getFieldDecorator } = this.props.form;
const { currentData,allClass,allSex } = this.props;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
return (
<>
<Form {...formItemLayout} onSubmit={this.handleSubmit}>
<Form.Item label="姓名">
{getFieldDecorator('name', {
// 校验规则
rules: [
{
required: true,
message: '不能为空',
},
],
// 文本框默认值
initialValue:currentData?currentData.name:''
})(<Input />)}
</Form.Item>
<Form.Item label="性别">
{getFieldDecorator('sex', {
rules: [
{
required: true,
message: '不能为空',
},
],
initialValue:allSex?allSex[0]:''
})(
<Select style={{ width: 120 }} onChange={this.handleChange} >
{
allSex.map(item=>{
return <Option value={item} key={item}>{item}</Option>
})
}
</Select>
)}
</Form.Item>
<Form.Item label="班级">
{getFieldDecorator('classGrade', {
rules: [
{
required: true,
message: '不能为空',
},
],
initialValue:allClass?allClass[0]:''
})(
<Select style={{ width: 120 }} onChange={this.handleChange} >
{
allClass.map(item=>{
return <Option value={item} key={item}>{item}</Option>
})
}
</Select>
)}
</Form.Item>
</Form>
</>
);
}
}
// 经 Form.create() 包装过的组件会自带 this.props.form 属性,具有自动收集数据并校验的功能
export default Form.create()(AddOrUpdate);
在getFieldDecorator中有多种功能的Api,大家可自行查看官方文档:(https://3x.ant.design/components/form-cn/#components-form-demo-validate-other)。
下面着重说一下存在多个子组件使用Form表单的情况,如下所示:
多个子组件
return (
{/* 子组件1-基础信息 */}
<BaseInfo {...this.props} data={baseData}/>
{/* 子组件2-产品信息 */}
<ProductList {...this.props} data={productData} />
{/* 子组件3-弹窗 触发条件为子组件2中的方法 */}
<Modal
title="审核"
visible={visible}
onCancel={()=>{
this.setState({visible:false})
}}
onOk={this.onOk}
>
<CheckModal />
<Modal/>
)
假设,在子组件1-BaseInfo、子组件2-ProductList、子组件3-CheckModal中均使用了Form表单,且都进行了rules必填验证,如何实现在弹窗出现时时,只对子组件3-CheckModal进行表单必填验证?
<Form.Item
label="xxx"
{getFieldDecorator('x',{
{/* 必填项 */}
rules:[{required:true, message:'xxx必填'}]
})(
<Input />
)}
>
<Form.Item/>
使用ref获取子组件
在这里可以使用React的ref属性,ref可以挂载到组件上或DOM元素上,挂载在DOM元素上时,返回的是具体的DOM节点;挂载在组件上时,返回的是组件实例。
var Parent = React.createClass({
render: function(){
return (
<div className = 'parent'>
<Child ref = 'child'/>
</div>
)
},
componentDidMount(){
console.log(this.refs.child); // 访问挂载在组件上ref
console.log(this.refs.child.refs.update); // 访问挂载在dom元素上的ref
}
})
var Child = React.createClass({
render: function() {
return (
<div ref="test">
<a ref="update">更新</a>
</div>
);
}
});
ReactDOM.render(
<Parent/>,
document.getElementById('example')
);
新版本的React已经不推荐我们使用ref string(ref=’ '),而是使用ref callBack(ref=>this.modalRef = ref)。
最终结果如下:
单独验证子组件
onOk=()=>{
// 只对当前组件是否必填进行校验
this.checkModal.validateFields((err,value)=>{
if(err) {return}
})
}
return (
{/* 子组件1-基础信息 */}
<BaseInfo {...this.props} data={baseData}/>
{/* 子组件2-产品信息 */}
<ProductList {...this.props} data={productData} />
{/* 子组件3-弹窗 触发条件为子组件2中的方法 */}
<Modal
title="审核"
visible={visible}
onCancel={()=>{
this.setState({visible:false})
}}
onOk={this.onOk}
>
{/* 使用ref callBack获取组件 */}
<CheckModal ref={ref => this.checkModal = ref}/>
<Modal/>
)