this.props.form属性
[
"getFieldsValue",
"getFieldValue",
"getFieldInstance",
"setFieldsValue",
"setFields",
"setFieldsInitialValue",
"getFieldDecorator",
"getFieldProps",
"getFieldsError",
"getFieldError",
"isFieldValidating",
"isFieldsValidating",
"isFieldsTouched",
"isFieldTouched",
"isSubmitting",
"submit",
"validateFields",
"resetFields",
"validateFieldsAndScroll"
]
获取表单的值
const KEY = 'DATA'
const values = form.getFieldValue(KEY); // 不带 key属性
// 等价于 const valeus = form.getFieldsValue()[KEY]
const valeus = form.getFieldsValue() // 带 key属性
const valeus = form.getFieldsValue()[KEY]
- 一个 input框改变,整个 Form内的所有表单都会重新渲染
- 4.x不会全部渲染,只会渲染改变的表单
- 3.x表单优化点,拆分表单,表单不要太大,否则影响性能
- https://redux-form.com/
initialValue细节
form中的select初始值为空时 要用 undefined
initialValue: this.props.isTrue ? this.props.data.bank.id : undefined
form中的日期datePicker相关的日期的空,初始值要用 null
initialValue: null
表单的重要属性
- value;input,textarea,select组件值为 value
- checked;radio,checkbox的组件值为 boolean
- selected;select组件下的option,不推荐的用法
- 推荐在select组件上使用value
- antd表单脚校验,把所有的数据都托管给 form
- data = form.getFieldValues() 获取到所有的数据
- 数据仅仅提供一个初始值 initialvalue
- form表单就像一个黑盒,不管数据如何变化,都不会影响到外面的
// 只读属性,错误的用法 <Input readonly="readonly" />
<Input readOnly />
- form表单就像一个黑盒,不管数据如何变化,都不会影响到外面的
直接返回值的表单
InputNumber
Switch
Select
直接 event事件
Radio
Input
Form.create()()
- 使用Form自带的收集校验功能,需要使用Form.create()包装组件,每一个需要收集的值还需要getFieldDecorator进行注册
- 了解Form表单的本质,更好的使用的Form表单 ```jsx // class 组件的 Form.create class CustomForm extends React.PureComponent {}
export default Form.create({})(CustomForm)
// 函数组件的 Form.create const CustomForm =(props)=>{ const { form } = porps } export default Form.create()(CustomForm)
<a name="moUdt"></a>
### mapPropsToFields
```jsx
const FormContainer = Form.create({
mapPropsToFields(props) {
return {
username: Form.createFormField({
...props.username,
value: props.username.value,
errors: !props.name ? [{message: '请输入名字'}] : null
}),
};
}
})(MyForm)
手动设置错误提示
// 验证表单
onSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((error, values) => {
if (error) return // error=null 通过验证
setTimeout(() => {
// server validate
if (values.user === 'yiminghe') {
this.props.form.setFields({
user: {
value: values.user,
errors: [new Error('用户名不存在')],
},
});
}
}, 500)
})
}
render () {
const { getFieldDecorator } = this.props.form
return (
<Form onSubmit={ this.onSubmit}>
<Form.Item>
{ getFieldDecortator('name')(<Input placeholder=""/>) }
</Form.item>
</Form>
)
}
Form相关名词
- labelCol:指定label的 col宽度
- wrapCol:指定表单输入控件的 col宽度
- span跨距
- col应该是column的缩写,表示列
- wrapper包装器
- offset是表示偏移量,一个东西相对另一个的距离
Form.item同名问题
- 同名表单项的值共享,并且其中一个的值改变,另外的同名表单的值也一致改变
- 同名的表单被当成完全相同的表单项处理,数值和onChange行为都一样,
- 两个表单项的onChange始终只执行了最后一个表单项的onChange事件
- 动态新增表单会出现表单同名的问题
- Form.Item中 name的唯一性
- 要想得到正确的结果,应该分别分别修改两个表单中的域名
Form.Item内多个表单
Form.Item内嵌套 Form.Item多个输入框
<FormItem
{...formItemLayout}
label={'员工信息'}
>
<Row gutter={16}>
<Col span={8}>
{this.props.form.getFieldDecorator('name', {
rules: [{ required: true, message: '请输入姓名' }],
})(<Input placeholder={'请输入'} />)}
</Col>
<Col span={8}>
<FormItem>
{this.props.form.getFieldDecorator('user', {
rules: [{
required: true,
// tslint:disable-next-line:max-line-length
pattern: /(^[1-9][0-9](\.\d)?$)|(^[1-9](\.\d)?$)|(^0\.\d$)/,
message: '请输入收入',
}],
})(<Input addonAfter={'%'} placeholder={'请保留一位小数'}/>)}
</FormItem>
</Col>
</Row>
</FormItem>
Switch
- getFieldDecorator包裹下的Switch组件无法显示为true状态
- Switch组件是通过checked的属性来显示状态的,所以需要一个额外的属性 valuePropName
- antd Switch 报错:[antd: Switch]
value
is not validate prop, do you meanchecked
?- Switch 组件是通过 checked 的属性来显示状态的,添加一个额外的属性
valuePropName
<FormItem {...formItemLayout} label="是否显示">
{getFieldDecorator('show', {
initialValue: true,
valuePropName: 'checked'
})(<Switch />)}
</FormItem>
- Switch 组件是通过 checked 的属性来显示状态的,添加一个额外的属性
valuePropName
- Checkbox
- Switch
- initialValue 必须是 Boolean值 true / false
Textarea
<Input.Textarea
placeholder=""
cols=""
rows=""
maxlength=""
minlength=""
name="textarea"
/>
// css
textarea {
resize: none; // 禁止拖拽
}
textarea里面内容换行
- 添加
<br>
,或 ‘\n’ 是没有用的 - 利用html换行符
或
- 普通文本换行,加上 ‘\n’ 或 ‘\r\n’
Form验证
- form动态校验解决方案:使用form.validateFields([key], { force: true })来解决
- 自定义验证规则中必须callback一个信息回来,即每种判断情况都要加callback
- form组件的的 validateFields或者 validateFieldsAndScroll方法时如果它没有进入方法而是直接跳过了方法
- 看下:自定义验证方法validator代码块里是否有某一条分支没有执行 callback 函数
- 官方规定:自定义校验 callback 必须被调用
- 调callback(null)相当于没执行callback; callback(undefined)相当于callback()
- 使用 validator 时,其方法体总是需要调用 callback()
- https://github.com/ant-design/ant-design/issues/5155
- Antd组件的坑 https://segmentfault.com/a/1190000015240590
- Form表单封装 https://closertb.site/blog/54
Form隐藏 *
hideRequireMark
rules 验证规则
// 必须是数字
rules: [
{
required: true,
pattern: new RegExp(/^[1-9]\d*$/, 'g'),
message: placeholder + number,
},
]
自定义验证
phoneValidator = (rule, value, callback) => {
const regexp = new RegExp(/^1(3|4|5|6|7|8)\d{9}$/);
if (value && !regexp.test(value)) {
callback('手机号格式错误!');
} else {
callback();
}
}
handleConfirmPassword(rule, value, callback) {
if (value && value.length < 5 ) {
return callback('长度不足');
}
// Note: 必须总是返回一个 callback,否则 validateFieldsAndScroll 无法响应
callback()
}
<FormItem {...formItemLayout} label="确认密码">
{
getFieldDecorator('confirmPassword', {
rules: [{
required: true,
message: '请再次输入以确认新密码',
}, {
validator: this.handleConfirmPassword
}],
})(<Input type="password" />)
}
</FormItem>
Form表单常见问题
验证丢失
- 数据存在上层组件,需要从父组件传递过来
- 需要从redux中回填错误信息去显示表单校验错误,否则重新渲染,丢失验证错误
动态表单嵌套
- 导致表单输入很卡的话,是不是页面比较复杂?
- 如果可以,表单部分在单独组件内与 redux connect
- 不需用的 props 尽量不要传递(比如少用 {…this.props} )
- 组件实现 shouldComponentUpdate 优化
SearchForm 搜索表单
- SearchForm/index.js
- export default SearchForm.js ```jsx import React, { Component } from ‘react’ import { Form, Input, Select, Checkbox, DatePIcker, Button } from ‘antd’
const { Item } = Form const { Option } = Select
const initOption = data => { if (!Array.isArray(data) || !data.length) return data
return data.map(item => ) }
const data = [ { type: ‘Select’, label: ‘选择城市’, labelWidth: ‘100’, field: ‘city’, initialValue: ‘’, children: [ { name: ‘lucy’, id: 1 } ], rules: [] } ]
class SearchForm extends Component {
initForm = () => { const { getFieldDecorator } = this.props.form const data = this.props.data const FormItem = []
// 如果不是数组,直接返回
if (!Array.isArray(data) || !data.length) return data
data.forEach((item, i) => {
let { type='input', label, labelWidth='90px', field,
initialValue='', placeholder='', htmlType='text',
children= [], } = item
console.log('type', field)
switch(type.toLocaleLowerCase()) {
case 'select':
const SELECT = (
<Item label={label} key={field}>
{
getFieldDecorator([field], {
initialValue
})(
<Select
type='text'
placeholder={placeholder}
style={{width: labelWidth}}
>
{ initOption(children) }
</Select>
)
}
</Item>
)
FormItem.push(SELECT)
break;
default:
const INPUT = (
<Item label={label} key={field}>
{
getFieldDecorator(field, {
initialValue
})(
<Input
type={htmlType}
placeholder={placeholder}
style={{width: labelWidth}}
/>
)
}
</Item>
)
FormItem.push(INPUT)
}
})
return FormItem
}
render () { return (
) }// 获取 Form表单的值 onSubmit = () => { const data = this.props.form.getFieldsValue() this.props.onSubmit(data) }
onReset = ()=> { this.props.form.resetFields() } }
export default Form.create()(SearchForm)
<a name="U2oty"></a>
### 使用 SearchForm.js
```jsx
// 使用组件
import BaseFrom from './BaseForm/index.js'
<BaseForm
data={ [] }
onSubmit={ this.onSubmit }
/>
封装 v3-form
import React from 'react';
import {
Row, Col,
Form, Input, Switch, Radio,
} from 'antd';
// import { FormattedMessage } from 'react-intl';
const options = [
{ label: '线上日志', value: '1' },
{ label: '手工输入', value: '0' },
]
// 列选择
const LogModalForm = props => {
const {
fnAction, keyFocus,
} = props;
const {getFieldDecorator} = props.form
// getFieldDecorator('from', { initialValue: '0' });
// 报错
// getFieldDecorator('switch', { valuePropName:'checked', initialValue: true });
const itemLayout = {
labelCol: {
span: 5
},
wrapperCol: {
span: 19
}
}
return (
<>
<Form {...itemLayout}>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="列名">
{getFieldDecorator('columnname', {
rules: [
{
required: true,
message: 'Please input your password!',
},
],
})(<Input />)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="默认值">
{getFieldDecorator('d-value', {
initialValue: '',
rules: [
{
required: true,
message: 'Please input your password!',
},
],
})(<Input />)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="日志来源">
{getFieldDecorator('from', {
initialValue: '1',
})(<Radio.Group options={options} />)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="选取规则">
{getFieldDecorator('columnname', {})(<Input />)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="列值翻译">
{getFieldDecorator('switch', {
valuePropName:'checked',
initialValue: true,
})(<Switch />)}
</Form.Item>
</Col>
</Row>
</Form>
</>
);
};
// mapPropsToFields
export default Form.create({
name: 'logFilter',
onValuesChange (props, value) {
console.log('change', props, value)
},
})(LogModalForm)
右侧搜索框
表单设置默认值
Form.createFormField
{nickname: Form.createFormField({value: “你好呀”})}
import React, { PureComponent } from 'react'
import { Form, Input, Button,
Select, Switch
} from 'antd';
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 8 },
};
const formTailLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 8, offset: 4 },
};
const style = {
margin: '30px auto',
background: 'rgba(200, 200, 200, 0.2)',
width: '800px'
}
// {column: "0", method: "10", diff: "0", nickname: "123", switch: true}
class MyForm extends PureComponent {
state = {
checkNick: false,
};
check = () => {
this.props.form.validateFields(err => {
console.log('check', err)
if (err) { // null 说明验证通过
return console.info('表单验证失败', this.props);
}
const {form} = this.props
const data = form.getFieldsValue()
console.log('sumibt2000', data)
});
};
handleChange = e => {
this.setState(
{
checkNick: e.target.checked,
},
() => {
this.props.form.validateFields(['nickname'], { force: true });
},
);
};
handleSubmit = ev => {
const {form} = this.props
const data = form.getFieldsValue()
console.log('data', data)
console.log('sumibt2000', ev, data)
ev.preventDefault();
return false
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form
{...formItemLayout}
onSubmit={this.handleSubmit}
style={style}>
<Form.Item label="取值列" hasFeedback>
{getFieldDecorator('column', {
rules: [{ required: true, message: 'Please select your country!' }],
})(
<Select placeholder="取值列">
<Select.Option value="0">consume</Select.Option>
<Select.Option value="1">provider</Select.Option>
</Select>,
)}
</Form.Item>
<Form.Item label="取值方式" hasFeedback>
{getFieldDecorator('method', {
rules: [{ required: true, message: 'Please select your country!' }],
})(
<Select placeholder="取值方式">
<Select.Option value="10">当前时间的值</Select.Option>
<Select.Option value="11">最近 N分钟的值</Select.Option>
</Select>,
)}
</Form.Item>
<Form.Item label="对比阈值方式" hasFeedback>
{getFieldDecorator('diff', {
rules: [{ required: true, message: 'Please select your country!' }],
})(
<Select placeholder="对比阈值方式">
<Select.Option value="0">大于</Select.Option>
<Select.Option value="1">小于</Select.Option>
{/* {false} // noData */}
</Select>,
)}
</Form.Item>
<Form.Item {...formItemLayout} label="阈值">
{
getFieldDecorator('nickname', {
rules: [
{
required: this.state.checkNick,
message: '请输入阈值',
// required: true,
// pattern: new RegExp(/^[0-9]\d*$/, 'g'),
},
],
}
)(<Input placeholder="请输入阈值" />)}
</Form.Item>
<Form.Item label="Switch">
{getFieldDecorator('switch', { valuePropName: 'checked' })(<Switch />)}
</Form.Item>
<Form.Item {...formTailLayout}>
{/* <Button type="primary" htmlType="submit"> 触发 onSubmit方法 */}
<Button type="primary" onClick={this.check}>
Check
</Button>
</Form.Item>
</Form>
);
}
}
export default Form.create({
name: 'my-form',
mapPropsToFields (props) {
console.log('props-create', props)
return {
// 如果是数字0,直接显示在 select框里面,不会选中
column: Form.createFormField({value: "0"}),
// method: "10",
// diff: "0", // 报错 You must wrap field data with `createFormField`.
nickname: Form.createFormField({value: "你好呀"}),
switch: Form.createFormField({value: true})
}
}
})(MyForm)
/**
使用this.form.setFieldsValue时,赋值的数据要一一匹配field,
用不到的不要赋值即可,错误提示就不再提醒了
*/
Select
placeholder不显示问题
- form中的 Select初始值为空时 要用 undefined
- 设置
initialValue:undefined
- 需要设置value为undefined,placeholder可正常显示
- 设置
- 日期datePicker相关的日期的空,初始值要用 null
- 回显或者新增表单数据时,空字符串 ‘’, 或者 null, 都不管用
- input 表单设置 ‘’ 空字符串有用
Select滚动分离
- 外层页面一滚动,下拉框与下拉内容就分离了,分离离了
- 就是因为 antd所有的弹框都是默认挂载在body下面,然后绝对定位的。所以滚动body内容, 就会造成与弹出触发的那个组件错位。
- 3.x以后 antd对这个bug做出了一个解决方案,就是增加了getPopupContainer属性,可以让下拉内容挂载任何存在的dom节点上,并相对其定位
- 案例:https://codesandbox.io/s/4j168r7jw0?file=/index.js:0-849
<div style={{ position: 'relative' }} id="area">
<Select
defaultValue="lucy"
style={{ width: 120 }}
getPopupContainer={() => document.getElementById('area')}
>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
</Select>
</div>
select报错
- Uncaught Error: Need at least a key or a value or a label (only for OptGroup) for [object Object] at getValuePropValue (util.js:28)
- antd的Select组件的时候,出现报错
- 原因:用select组件没有写value值
- 至少需要一个key或者value或者是一个label
- Select组件里面直接渲染Option,不能再把 Option抽离为组件,否则也报错