Happy to use react-formutil in the project based on react-bootstrap
^_^
在 react-bootstrap 项目,结合react-formutil 来快速构建表单。
如果你在使用其他 react 组件库,可以查阅:
- ant-design
react-antd-formutil
- react-md
react-md-formutil
- Material-UI
react-material-formutil
- 安装 Installation
- 使用 Usage
- FAQ
📌📌 该版本适用于 bootstrap@5.x
和 react-bootstrap@2.x
。
- 如果你项目中使用的是
bootstrap@3.x
和react-bootstrap@0.32.x
,请查看 react-bootstrap-formutil@classic - 如果你项目中使用的是
bootstrap@4.x
和react-bootstrap@1.x
,请查看 react-bootstrap-formutil@bs4
# npm npm install react-bootstrap-formutil --save # yarn yarn install react-bootstrap-formutil
react-bootstrap-formutil
整合了react-formutil
的组件,所以可以直接从react-bootstrap-formutil
中导出所需要的react-formutil
组件。不用单独从react-formutil
中导出。
本文档适用于 bootstrap@4.x
和 react-bootstrap@1.x
版本,如果你要查看早期react-bootstrap@0.32
的文档,请查看 react-bootstrap-formutil@classic
先看一个使用示例(点击查看在线完整示例 : react-bootstrap-formutil on codesandbox.io):
import React, { Component } from 'react'; import { withForm, FormGroup } from 'react-bootstrap-formutil'; import { FormControl } from 'react-bootstrap'; // 导入react-bootstrap的FormControl组件 @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}> <FormGroup name="username" required> <FormControl type="text" /> </FormGroup> </form> ); } }
FormGroup
是 react-bootstrap-formuitl
新增加的组件,withForm
是react-formutil
的组件(没错,你可以直接从react-bootstrap-formutil
中导出react-formutil
的组件啦)。
只需要将react-bootstrap
的交互组件,嵌套在FormGroup
下,即可实现自动的表单状态同步。
要实现将react-bootstrap
的交互组件的值能同步到 react-formutil
的状态中,需要通过 FormGroup
这个组件来实现中间态绑定。
FormGroup
提供了与react-bootstrap
中同名组件一样的 UI 与状态展示功能,所以你完全可以使用react-bootstrap-formutil
中的FormGroup
替代react-bootstrap
中的该组件。
如果给
FormGroup
传递了多个子节点,可能会出现无法非预期的异常情况。你可以了解如何正确的使用FormGroup嵌套渲染多个节点元素?
。
FormGroup
基于react-formutil
中的<EasyField />
组件实现,同名属性参数用法可以更多参考<EasyField />
。
设置输入项的 name 值,表单项将会以 name 作为 key 收集到 formutil 的状态中。支持嵌套语法 (同react-formutil
的Field
同名参数,可以参考 name)
设置该表单项的默认值 (同react-formutil
的Field
同名参数,可以参考$defaultvalue)
设置校验方法 (同react-formutil
的Field
同名参数 , 可以参考 $validators)
同 react-formutil 的 EasyField,FormControl 也内置了同样的校验规则:
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
匹配错误信息。
如果表单项需要 label,应该通过该属性设置。你可以传入字符串,或者直接传入Form.Label
节点:
<FormGroup name="name" label="Username"> <Form.Control /> </FormGroup> //or <FormGroup name="name" label={<Form.Label>Username</Form.Label>}> <Form.Control /> </FormGroup>
如果要设置表单项的 helper,需要通过该字段设置,或者直接传入HelpBlock
节点:
<FormGroup name="name" helper="Please type your name"> <FormControl /> </FormGroup> //or <FormGroup name="name" helper={<Form.Text className="text-muted">Please type your name</Form.Text>}> <FormControl /> </FormGroup>
该属性可用来设置 label 的 栅格
布局:
<FormGroup name="agree" label="Name" wrapperCol={{ xs: 12, md: 10 }} labelCol={{ xs: 12, md: 2 }}> <Control /> </FormGroup>
该属性可用来搭配labelCol
设置水平布局的表单项的 栅格
布局
指定该属性,启用Floating Labels
效果
<FormGroup label="Your Name" floatingLabel={true}> <Control placeholder="text here" /> </FormGroup>
该属性可以用来实现InputGroup
布局,该属性包含三个字段pre
end
size
,可以分别用来添加前后addons
以及设置InputGroup
的尺寸大小。
如果pre
end
的值为字符串,则会自动添加InputGroup.Text
组件包装。当然,你也可以自行定义要添加的节点。
<FormGroup name="email" addons={{ size: 'lg', // 如果是字符串,会自动添加 InputGroup.Text 包装 pre: 'Email', // 也可以直接传递节点 end: <InputGroup.Text>@apple.com</InputGroup.Text> }}> <FormControl /> </FormGroup>
该属性用来添加一个额外的节点内容。
<FormGroup name="email" extra={<div>其他内容</div>}> <FormControl /> </FormGroup> // 你可以利用 Fragment 添加多个其他节点内容 <FormGroup name="email" extra={ <Fragment> <p>...</p> <div>...</div> </Fragment> }> <FormControl /> </FormGroup>
注意: 这里介绍的$parser
和$formatter
为react-formutil@>0.5.0
的用法。如果你还在使用0.5.0
以前的版本,请参考安装0.5.0
版本以上的react-formutil
;
当用户在表单中进行输入时(主动更新视图),视图中的值在更新到状态模型中前,会经过 $parser
处理。
// 通过$parser属性来过滤前后输入空格 <FormGroup name="fieldName" $parser={(viewValue, $setViewValue) => viewValue.trim()}> //... </FormGroup>
注意,上述写法不会修改当前视图值,它仅仅影响状态模型中的值。如果希望限制用户的输入(例如禁止用户输入任意空格),可以通过$parser
的第二个参数$setViewValue
,来在用户每次输入后立即更新视图值。
// 通过$parser属性来过滤前后输入空格 <FormGroup name="fieldName" $parser={(viewValue, $setViewValue) => $setViewValue(viewValue.trim())} />
当在表单模型中主动更新模型值时,会通过 $formatter
将模型中的值转换为$viewValue
后传递给视图渲染。
// 通过$formatter将模型中的值转换为标准的金额书写格式 <FormGroup name="amount" $formatter={(value, $setModelValue) => priceFormat(value)} />
$formatter
同样有一个回调方法$setModelValue
,它可以用来在处理模型值时再次对其进行修改。
可以用来优化表单的校验速度,请参考: $validateLazy
可以用来优化当前表单项的性能,避免过多的重复渲染。如果你遇到了表单性能问题,可以尝试该属性来改善。
详细解释和使用、注意事项请参考: $memo
对于 <Form.Check />
,其值默认是 checked 属性,为布尔值。可以通过checked
unchecked
来设置 checked 状态时所要映射的值:
<FormGroup checked="yes" unchecked="no"> <Form.Check type="checkbox" /> </FormGroup>
该示例中, 当 Switch 为开时,获取的值将为 yes。
设置校验结果的错误信息。
<FormGroup name="username" required validMessage={{ required: '请输入用户名' }}> <FormControl /> </FormGroup>
布尔值。设置是否要支持额外的状态,如果设置为true
,这表单项正确时将会显示 绿色的valid
状态
<FormGroup name="username" feedback> <FormControl /> </FormGroup>
该四个参数可以用来设置绑定到组件上的值或者值变动、是否聚焦等事件回调。该项一般不需要设置,FormGroup
已经针对 react-bootstrap
中的所有 data-entry
型组件做了兼容处理。
对于一些特殊场景,例如不需要同步 focus
、blur
,则可以通过将该值设为{null}
来禁用:
//禁用focus、blur状态同步 <FormGroup focusPropName={null} blurPropName={null} name="username"> <FormControl /> </FormGroup>
这几个属性同react-bootstrap
中 FormGroup
组件的同名属性,事实上任何可以传递给Form.Group
的属性都可以传递。
该属性从
v1.1.0
起可
noStyle
可以用来控制是否输出FormGroup
的额外的样式元素。缺省情况下默认值为false
。
当noStyle
为true
时,将会只渲染字段节点本身,但是其表单状态依然会被处理收集。此时,如果其存在父级嵌套的FormGroup
,那么其表达校验状态将会传递给父级的FormGroup
来展现。
这对于连续的紧凑型表单元素将非常有用!可以避免校验错误描述信息都堆叠在一起! 但是没有额外的样式显示,包括表单校验状态都无法显示了。此时可以在其外层包裹一层不带name
的FormGroup
,这些noStyle
的表单项就会把他们自身的状态向上进行注册显示了!
但是有以下几点需要注意:
- 最外层的
FormGroup
不能设置name
属性,否则将不会被当作子级的校验状态容器 - 内层的
FormGroup
需要添加相应的name
值(向表单控制器注册自身)以及noStyle
属性(不渲染额外的样式,避免和上层冲突)
// 这里不能设置name <FormGroup label="姓名"> <InputGroup> {/* 与普通的FormGroup用法一致,只是多了个noStyle */} <FormGroup name="demo4.first_name" required validMessage={{ required: 'First name reuqired!' }} noStyle> <Form.Control placeholder="姓" /> </FormGroup> <FormGroup name="demo4.last_name" required validMessage={{ required: 'Last name reuqired!' }} noStyle> <Form.Control placeholder="名" /> </FormGroup> </InputGroup> </FormGroup>
以上运行示例请参考 示例 demo4
用来覆盖全局的 errorLevel 设置。参考setErrorLevel(level)
该组件用来同步多选组,需要嵌套在FormGroup
下配合使用:
每个子项
Form.Check
组件必须显式设置value
属性值
<FormGroup name="group.checkbox" required label="Checkbox group"> <CheckboxGroup> <Form.Check inline type="checkbox" value="1" label="1" /> <Form.Check inline type="checkbox" value="2" label="2" /> <Form.Check inline type="checkbox" value="2" label="3" /> </CheckboxGroup> </FormGroup>
该组件用来同步单选组,需要嵌套在FormGroup
下配合使用:
每个子项
Form.Check
组件必须显式设置value
属性值
<FormGroup name="group.radio" required label="Radio group"> <RadioGroup> <Form.Check inline type="radio" value="1" label="1" /> <Form.Check inline type="radio" value="2" label="2" /> <Form.Check inline type="radio" value="2" label="3" /> </RadioGroup> </FormGroup>
该组件用来同步单选组,需要嵌套在FormGroup
下配合使用:
每个子项
Form.Check
组件必须显式设置value
属性值
<FormGroup name="group.switch" required label="Switch group"> <SwitchGroup> <Form.Check inline type="switch" value="1" label="1" /> <Form.Check inline type="switch" value="2" label="2" /> <Form.Check inline type="switch" value="2" label="3" /> </SwitchGroup> </FormGroup>
setErrorLevel
该方法可以用来全局设置错误信息何时出现,有三个级别可以设置:
0
当$dirty
$touched
invalid
都为 true 时1
当$dirty
invalid
都为 true 时2
当invalid
为 true 时off
关闭错误显示
默认值为 1
注意,该方法影响全局,如果只是希望单独对某个表单项进行设置,可以通过
errorLevel
属性进行设置:参考errorLevel
import { setErrorLevel } from 'react-bootstrap-formutil'; setErrorLevel(0); // 当关闭错误显示时,errorLevel='off',你可以手动自行设置错误展示方式: <FormGroup name="errorOff" errorLevel="off" validationState={$formutil.$errors.errorOff ? 'error' : null} helper={$formutil.$errors.errorOff ? <div>出错啦</div> : null}> <FormControl /> </FormGroup>;
<FormGroup name="pwd"> <FormControl type="password" /> </FormGroup> <FormGroup name="pwd"> <FormControl as="select"> <option value="1">1</option> <option value="2">2</option> </FormControl> </FormGroup>
<FormGroup name="agree"> <Form.Check type="checkbox" /> </FormGroup> <FormGroup name="agree"> <Form.Check type="radio" /> </FormGroup> <FormGroup name="agree"> <Form.Check type="switch" /> </FormGroup>
需要通过 addons
属性调用
<FormGroup name="name" addons={{ pre: '@' }}> <FormControl type="text" /> </FormGroup>
注意:ToggleButton
组件必须指定id属性!! 您可以查看react-boostrap
文档详细了解。
<FormGroup name="hobbies"> <ToggleButtonGroup type="checkbox"> <ToggleButton value={1} id="t1">option 1</ToggleButton> <ToggleButton value={2} id="t2">option 2</ToggleButton> <ToggleButton value={3} id="t3">option 3</ToggleButton> </ToggleButtonGroup> </FormGroup>
注意:这三个组件并不是react-bootstrap
提供的组件,而是react-bootstrap-formutil
提供的。
import { CheckboxGroup, RadioGroup, SwitchGroup } from 'react-bootstrap-formutil';
FormGroup
会自动给表单节点增加与该表单项校验状态相关的 className:
has-error
is-invalid
is-valid
is-touched
is-untouched
is-focused
is-unfocused
is-dirty
is-pristine
FormGroup
会覆盖掉直接添加到 react-bootstrap 组件上的onFocus
onBlur
onChange
方法,所以如果需要这三个事件方法,需要添加到 FormGroup
上:
<FormGroup name="test" onChange={ev => console.log('change', ev)} onFocus={ev => console.log('focus', ev)}> <FormControl /> </FormGroup>
如果在生产环境,发现例如Form.Check
ToggleButtonGroup
等组件无法正确捕获用户输入的值,这种情况一般是由于项目中使用了babel-plugin-import
插件。
react-bootstrap-formutil
中是使用 import { FormControl } from 'react-bootstrap'
这种写法来调用 FormControl
组件的,而babel-plugin-import
插件会将项目源代码中的类似语句,替换成import FormControl from 'react-bootstrap/lib/FormControl'
。这两种写法获取到的Switch
其实并不是严格意义上的相等,前者是对后者的又一层导出封装。
而由于babel-plugin-import
一般仅仅会配置成仅仅对项目代码进行处理,所以处于项目node_modules
目录中的react-bootstrap-formutil
中的语句不会被处理。我们需要通过修改项目 webpack 配置的方式,来使babel-plugin-import
插件能处理react-bootstrap-formutil
的代码。
可以编辑项目的 webpack 配置(只需要配置生产环境的构建配置即可),在rules
模块下添加以下的代码:
{ test: /\.(js|mjs)$/, include: /react-bootstrap-formutil/, // 仅仅处理react-bootstrap-formutil即可 loader: require.resolve('babel-loader'), options: { babelrc: false, plugins: [[ "import", { "libraryName": "react-bootstrap" }, "react-bootstrap" ]] } }
你可以通过给给children
属性传递render props
函数,来自由定义要渲染出的节点。但是请注意,当传递一个render props
函数时,需要手动绑定相关绑定事件和 value 属性!
该children
函数接受一个$fieldHandler
的对象,默认情况下其包含value
onChange
onFocus
onBlur
四个属性,但是如果你给FormItem
传递了valuePropName
等属性的话,这个值将会变为你通过valuePropName
所定义的名字。
更具体解释可以参考 react-formutil.$fieldHandler
<FormGroup name="username"> {$fieldHandler => ( <> <FormControl {...$fieldHandler} /> <div>其它节点内容</div> </> )} </FormGroup>