Happy to use react-formutil in the project based on ant-design@3
&4
^_^
在 ant-design 项目,结合 react-formutil 来快速构建表单。支持所有的ant-design
输入型(data-entry
)组件。
如果你在使用其他 react 组件库,可以查阅:
- react-bootstrap
react-bootstrap-formutil
- react-md
react-md-formutil
- Material-UI
react-material-formutil
react-antd-formutil
从1.0.0
版本开始,同时支持 Ant Design 3.x
和4.x
版本
# npm
npm install react-antd-formutil --save
# yarn
yarn install react-antd-formutil
react-antd-formutil
整合了react-formutil
的组件,所以可以直接从react-antd-formutil
中导出所需要的react-formutil
组件。不用单独从 react-formutil 中导出。
先看一个使用示例(点击查看在线完整示例: react-antd-formutil on codesandbox.io):
import React, { Component } from 'react';
import { withForm, FormItem } from 'react-antd-formutil';
import { Input, Form } from 'antd'; // 导入antd的Input组件
@withForm
class MyForm extends Component {
submit = () => {
const { $invalid, $getFirstError, $params } = this.props.$formutil;
if ($invalid) {
alert($getFistError());
} else {
// submit your data
}
};
render() {
return (
<Form onSubmit={this.onSubmit}>
<FormItem
name="username"
itemProps={{
label: 'Username'
}}>
<Input />
</FormItem>
</Form>
);
}
}
FormItem
是 react-antd-formuitl
新增加的组件,withForm
是react-formutil
的组件(没错,你可以直接从react-antd-formutil
中导出react-formutil
的组件啦)。
只需要将ant-design
的交互组件,嵌套在FormItem
下,即可实现自动的表单状态同步。
要实现将ant-design
的交互组件的值能同步到 react-formutil 的状态中,需要通过 FormItem 这个组件来实现中间态绑定。
它的作用有些类似 antd 中的getFieldDecorator
方法,但是用法比getFieldDecorator
更优雅,更 JSX 语法。FormItem
完全是标签声明式用法,它是对 antd 的Form.Item
组件的再次封装。
所以FormItem
会完整实现Form.Item
所可以显示的校验状态、错误暂时等 UI 变化。
如果给
FormItem
传递了多个子节点,可能会出现无法非预期的异常情况。你可以了解如何正确的使用FormItem嵌套渲染多个节点元素?
支持传递的属性
FormItem
可以接收所有antd
中的Form.Item
组件所接收的所有属性,另外还新增以下属性支持:
设置输入项的 name 值,表单项将会以 name 作为 key 收集到 formutil 的状态中。支持嵌套语法 (同react-formutil
的Field
同名参数,可以参考 name)
设置该表单项的默认值 (同react-formutil
的Field
同名参数,可以参考$defaultvalue)
设置校验方法 (同react-formutil
的Field
同名参数, 可以参考 $validators)
同 react-formutil 的 EasyField,FormItem 也内置了同样的校验规则:
required
必填required
maxLength
。最大输入长度,有效输入时才会校验maxLength="100"
minLength
最小输入长度,有效输入时才会校验minLength="10"
max
最大输入数值,仅支持 Number 比较。有效输入时才会校验max="100"
min
最小输入数值,仅支持 Number 比较。有效输入时才会校验min="10"
pattern
正则匹配。有效输入时才会校验pattern={/^\d+$/}
enum
枚举值检测。有效输入时才会校验enum={[1,2,3]}
checker
自定义校验函数。checker={value => value > 10 && value < 100 || '输入比如大于10小与100'}
注:校验属性的值为 null
时表示不进行该校验
内置的校验规则无需再次声明,除非规则不符合预期,需要替换,则可以通过$validators
传递同名校验方法即可替换默认的。另外,内置的校验规则,如果校验不通过,会尝试去 validMessage
匹配错误信息。
该属性为要传递给Form.Item
组件的配置项:
<FormItem
itemProps={{
label: 'Username',
colon: false
}}>
<Input />
</FormItem>
请参考react-formutil
中$parser
介绍。
请参考react-formutil
中$formatter
介绍。
对于 <Switch />
<Checkbox />
<Radio />
这三种组件,其值默认是 checked 属性,为布尔值。可以通过checked
unchecked
来设置 checked 状态时所要映射的值:
<FormItem checked="yes" unchecked="no">
<Switch />
</FormItem>
该示例中, 当 Switch 为开时,获取的值将为 yes。
可以用来优化表单的校验速度,请参考: $validateLazy
可以用来优化当前表单项的性能,避免过多的重复渲染。如果你遇到了表单性能问题,可以尝试该属性来改善。
详细解释和使用、注意事项请参考: $memo
设置校验结果的错误信息。
<FormItem
name="username"
required
validMessage={{
required: '请输入用户名'
}}>
<Input />
</FormItem>
该四个参数可以用来设置绑定到组件上的值或者值变动、是否聚焦等事件回调。该项一般不需要设置,FormItem
已经针对 antd
中的所有 data-entry
型组件做了兼容处理。
对于一些特殊场景,例如不需要同步 focus
、blur
,则可以通过将该值设为{null}
来禁用:
//禁用focus、blur状态同步
<FormItem focusPropName={null} blurPropName={null} name="username">
<Input />
</FormItem>
该属性从
v1.1.0
起可用该属性同时兼容
antd@3.x
和antd@4.x
,都可以使用!
noStyle
与AntDesign v4.0
中新版本的Form.Item
的noStyle
类似,可以用来控制是否输出Form.Item
的额外的样式元素。缺省情况下默认值为false
。
当noStyle
为true
时,将会只渲染字段节点本身,但是其表单状态依然会被处理收集。此时,如果其存在父级嵌套的FormItem
,那么其表达校验状态将会传递给父级的FormItem
来展现。
这对于连续的紧凑型表单元素将非常有用!可以避免校验错误描述信息都堆叠在一起! 但是没有额外的样式显示,包括表单校验状态都无法显示了。此时可以在其外层包裹一层不带name
的FormItem
,这些noStyle
的表单项就会把他们自身的状态向上进行注册显示了!
但是有以下几点需要注意:
- 最外层的
FormItem
不能设置name
属性,否则将不会被当作子级的校验状态容器 - 内层的
FormItem
需要添加相应的name
值(向表单控制器注册自身)以及noStyle
属性(不渲染额外的样式,避免和上层冲突)
// 这里不能设置name
<FormItem label="FormItem Group">
<Input.Group compact>
{/* 与普通的FormItem用法一致,只是多了个noStyle */}
<FormItem name="address.province" noStyle required validMessage={{ required: 'Province requird!' }}>
<Select placeholder="Select province">
<Select.Option value="Zhejiang">Zhejiang</Select.Option>
<Select.Option value="Jiangsu">Jiangsu</Select.Option>
</Select>
</FormItem>
<FormItem name="address.street" noStyle required validMessage={{ required: 'Street requird!' }}>
<Input style={{ width: '50%' }} placeholder="Input street" />
</FormItem>
</Input.Group>
</FormItem>
以上运行示例请参考 示例
用来覆盖全局的 errorLevel 设置。参考setErrorLevel(level)
setErrorLevel
该方法可以用来全局设置错误信息何时出现,有三个级别可以设置:
0
当$dirty
$touched
$invalid
都为 true 时1
当$dirty
$invalid
都为 true 时2
当$invalid
为 true 时off
关闭错误显示
默认值为 1
注意,该方法影响全局,如果只是希望单独对某个表单项进行设置,可以通过
errorLevel
属性进行设置:参考errorLevel
import { setErrorLevel } from 'react-antd-formutil';
setErrorLevel(0);
// 当关闭错误显示时,errorLevel='off',你可以手动自行设置错误展示方式:
<FormGroup
name="errorOff"
errorLevel="off"
itemProps={{
validateStatus: $formutil.$errors.errorOff ? 'error' : undefined,
help: $formutil.$errors.errorOff ? <div>出错啦</div> : null
}}>
<Input />
</FormGroup>;
支持Checkbox.Group
。
DatePicker
TimePicker
DatePicker.WeekPicker
DatePicker.MonthPicker
DatePicker.RangePicker
等几个日期类组件,都是深度结合了moment
使用的。如果希望收集到表单中的值是格式化好的时间字符串,可以通过$parser
$formatter
实现:
<FormItem name="datepicker" $parser={moment => moment.format('YYYY-MM-DD')} $formatter={date => moment(date)}>
<DatePicker />
</FormItem>
对于DatePicker.RangePicker
,由于其值是一个数组,所以需要这样处理:
<FormItem
name="datepicker"
$parser={moments => moments.map(moment => moment.format('YYYY-MM-DD'))}
$formatter={dates => dates.map(date => moment(date))}>
<DatePicker.RangePicker />
</FormItem>
Pagination
并非antd
所归纳的data entry
组件,但是其接口设计也可以支持FormItem
:
<FormItem name="page" $defaultValue={2}>
<Pagination pageSize={10} total={100} />
</FormItem>
支持Radio.Group
。
Switch
Checkbox
(不包括Checkbox.Group
) Radio
(不包括Radio.Group
)三个组件,可以通过给FormItem
传递checked
unchecked
属性来改变被勾选时所映射到表单状态中的值:
<FormItem checked="yes" unchecked="no">
<Switch />
</FormItem>
Transfer
收集到表单状态中的是targetKeys
。
参考 DatePicker
Upload
组件非常特殊,其接受fileList
对象作为整个组件的状态。而实际业务中,往往只需要获取上传文件的返回的地址,或者一组文件的地址。可以通过$parser
控制如何获取上传结果的值,并且可以通过$parser
的第二个回调方法$setViewValue
来控制fileList
对象,实现对文件上传数量的控制。
单个文件上传,获取单个文件上传地址
<FormItem
name="upload"
$formatter={url =>
url && [
{
url,
uid: url,
status: 'done',
name: url.split('/').slice(-1)[0]
}
]
}
$parser={(info, $setViewValue) => {
// 必不可少,限制只能上传一个文件
$setViewValue(info.fileList.slice(-1));
if (info.file.status === 'done') {
return info.file.response.url;
}
}}
itemProps={{ ...formItemLayout, label: 'Upload' }}
required>
<Upload {...uplodConfig}>
<Button>
<UploadOutlined /> Click to Upload
</Button>
</Upload>
</FormItem>
多文件列表上传,获取多个文件上传地址数组
<FormItem
name="upload"
$formatter={urls =>
urls &&
urls.map(url => ({
url,
uid: url,
status: 'done',
name: url.split('/').slice(-1)[0]
}))
}
$parser={(info, $setViewValue) => {
// 限制最大上传文件数量为3,如果不需要限制,可以移除该行,或者修改该值
$setViewValue(info.fileList.slice(-3));
return info.fileList.filter(file => file.status === 'done').map(file => file.url || file.response.url);
}}
itemProps={{ ...formItemLayout, label: 'Upload' }}
required>
<Upload {...uplodConfig}>
<Button>
<UploadOutlined /> Click to Upload
</Button>
</Upload>
</FormItem>
FormGroup
会自动给表单节点增加与该表单项校验状态相关的 className:
has-error
is-invalid
is-valid
is-touched
is-untouched
is-focused
is-unfocused
is-dirty
is-pristine
FormItem
会覆盖掉直接添加到 antd 组件上的onFocus
onBlur
onChange
方法,所以如果需要这三个事件方法,需要添加到 FormItem
上:
<FormItem name="test" onChange={ev => console.log('change', ev)} onFocus={ev => console.log('focus', ev)}>
<Input />
</FormItem>
经过 debug,在3.8.x
版本上,依然存在对RangePicker
设置onFocus
onBlur
会异常频繁触发(比如在光标经过日期选择面板中每个数字时)的问题。可以禁用onFocus
onBlur
状态同步:
<FormItem name="datepicker" focusPropName={null} blurPropName={null}>
<DatePicker.RangePicker />
</FormItem>
如果在生产环境,发现例如Checkbox
Radio
Switch
等组件无法正确捕获用户输入的值,这种情况一般是由于项目中使用了babel-plugin-import
插件。
react-antd-formutil
中是使用 import { Switch } from 'antd'
这种写法来调用 Switch
组件的,而babel-plugin-import
插件会将项目源代码中的类似语句,替换成import Switch from 'antd/lib/switch'
。这两种写法获取到的Switch
其实并不是严格意义上的相等,前者是对后者的又一层导出封装。
而由于babel-plugin-import
一般仅仅会配置成仅仅对项目代码进行处理,所以处于项目node_modules
目录中的react-antd-formutil
中的语句不会被处理。我们需要通过修改项目 webpack 配置的方式,来使babel-plugin-import
插件能处理react-antd-formutil
的代码。
可以编辑项目的 webpack 配置(只需要配置生产环境的构建配置即可),在rules
模块下添加以下的代码:
{
test: /\.(js|mjs)$/,
include: /react-antd-formutil/, // 仅仅处理react-antd-formutil即可
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
plugins: [[
"import",
{
"libraryName": "antd"
},
"antd"
]]
}
}
你可以通过给给children
属性传递render props
函数,来自由定义要渲染出的节点。但是请注意,当传递一个render props
函数时,需要手动绑定相关绑定事件和 value 属性!
该children
函数接受一个$fieldHandler
的对象,默认情况下其包含value
onChange
onFocus
onBlur
四个属性,但是如果你给FormItem
传递了valuePropName
等属性的话,这个值将会变为你通过valuePropName
所定义的名字。
更具体解释可以参考 react-formutil.$fieldHandler
<FormItem name="username">
{$fieldHandler => (
<>
<Input {...$fieldHandler} />
<div>其它节点内容</div>
</>
)}
</FormItem>