FormBinder ICE 表单粘合剂

如果项目中使用的是 1.x 版本的基础组件(@alifd/next),请在左侧导航顶部切换组件版本。

安装方法

  1. 在命令行中执行以下命令npm install @icedesign/form-binder@1.0.3 -S

ICE 表单数据获取方案。

说明:1. 如果使用的是 FormBinder 0.x 的版本,请移步到 0.x 参考文档。2. 如果使用的是 FormBinder 1.x 的版本,需要确保依赖的 react 版本在 16.2.0 以上,1.x 版本使用了 React.Fragments API,支持 FormBinderWrapper 组件返回多个节点。

安装和升级

  1. npm install @icedesign/form-binder

表单功能

  • 表单双向绑定

  • 表单值统一处理

  • 布局自由组合

  • 对数据的有效性进行验证

表单元素

表单元素指的是 ICE 基础组件以及业务组件中的 InputCheckboxSelectRangeDatePickerTimePickerNumberPickerSwitchUpload 等以及用户自定义的组件,它能够响应 onChange 等用来获取用户输入

参数(Props)

方法名说明类型默认值
validateFields校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件([fieldNames: string[]],callback(errors,values)) => void

FormBinderWrapper

整个表单的容器,支持传入 value 和 onChange 等属性,其中 value 会作为整个表单数据根节点,交由下层组件去获取、更新操作

经过 FormBinderWrapper 包装的组件,数据传递和同步将被接管,这意味着:

  • 你不需要使用表单元素的 onChange 来做同步,但还是可以继续监听 onChange 等事件

  • 你不能使用组件的表单元素的 valuedefaultValue 等属性来设置表单元素的值,但可以通过初始的 value 进行设置

  • 你不需要通过 setState 来动态更新表单的值,因为表单默认支持双向数据通信,但可以通过 setFieldValue 和 getFieldValue 来设置或者更新表单域的值

参数(Props)

属性参数说明类型默认值
value表单值object{}
onChange任一表单域的值发生改变时的回调function(value)() => {}
enableScrollErrorField全局校验时,是否开启滚动到报错表单位置booleanfalse
scrollErrorFieldTopOffset全局校验滚动到报错位置时,距离顶部的偏移值(适用于头部 fixed 的场景)number0

FormBinder

表单组件粘合剂,将其作为 FormBinderWrapper 的子组件,即可实现双向绑定特性,之后表单域的改变会通过 FormBinder 转发从而响应到 FormBinderWrapper 的 onChange 方法进行通信

FormBinder 支持的属性包含以下两部分:

  • 自定义规则

  • 检验规则

自定义规则

参数(Props)

属性参数说明类型默认值
rules校验规则,参考下方文档object[]
name​表单域名称string
setFieldValue设置一个输入控件的值Function(fieldName: string)
getFieldValue获取一个输入控件的值Function(fieldName: string)
triggerType指定合适的触发事件string'onChange'

校验规则

参数(Props)

参数说明类型默认值
enum枚举类型string
len字段长度number
max最大长度number
message校验文案string
min最小长度number
pattern正则表达式校验RegExp
required是否必选booleanfalse
transform校验前转换字段值function(value) => transformedValue:any
type内建校验类型string'string'
validator自定义校验function(rule, value, callback)
whitespace必选时,空格是否会被视为错误booleanfalse

内建校验类型,可选项自定义校验(注意,callback 必须被调用

推荐:

  • 建议统一使用 async-validator 的校验规则,尽量不要使用表单元素的相关检验属性,这样做有利于代码的可维护性和优雅。

  • 内建校验类型,可选项

  • 更多高级用法可参考 async-validator

FormError

自定义表单的报错信息,自定义报错信息时需要指定 name,以此来获取当前报错的表单域来源

参数(Props)

参数说明类型默认值
name​表单域名称string
style自定义样式对象object
className自定义样式类名string
render自定义渲染报错的组件和处理逻辑Function(errors):ReactNode

双向绑定协议

双向绑定协议指的是组件接收 valueonChange 两个参数,其用户输入值由 value 提供,当用户操作组件导致数据变更,组件会调用 onChange 并把新的 value 作为第一个参数传出。React 社区的大多数组件都遵守这个设计,如 @icedesign/baseInput, Select, Checkbox 等,如果你希望你的表单类组件能够接入 FormBinder ,请务必遵守这个协议。

代码示例

登录表单

普通的登录表单,可以自由组合布局,自定义排列标签和表单域

FormBinder ICE 表单粘合剂 - 图1

查看源码在线预览

  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
  4. import { Input, Button, Checkbox, Feedback } from '@icedesign/base';
  5. class Login extends Component {
  6. constructor(props) {
  7. super(props);
  8. this.state = {
  9. value: {
  10. username: '',
  11. password: '',
  12. checkbox: false
  13. }
  14. };
  15. }
  16. formChange = value => {
  17. // 说明:
  18. // 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
  19. // 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
  20. this.setState({ value });
  21. };
  22. validateFields = () => {
  23. const { validateFields } = this.refs.form;
  24. validateFields((errors, values) => {
  25. console.log({ errors })
  26. if (!errors) {
  27. Feedback.toast.success('登录成功')
  28. }
  29. });
  30. }
  31. render() {
  32. return (
  33. <div style={styles.container}>
  34. <FormBinderWrapper
  35. value={this.state.value}
  36. onChange={this.formChange}
  37. ref="form"
  38. >
  39. <div style={styles.content}>
  40. <div style={styles.formItem}>
  41. <span style={styles.formItemLabel}>名称:</span>
  42. <FormBinder name="username" required message="请输入正确的名称" >
  43. <Input />
  44. </FormBinder>
  45. <FormError style={styles.formItemError} name="username" />
  46. </div>
  47. <div style={styles.formItem}>
  48. <span style={styles.formItemLabel}>密码:</span>
  49. <FormBinder name="password" required message="请输入正确的密码">
  50. <Input htmlType="password" />
  51. </FormBinder>
  52. <FormError style={styles.formItemError} name="password" />
  53. </div>
  54. <div style={styles.formItem}>
  55. <span style={styles.formItemLabel}>记住密码:</span>
  56. <FormBinder
  57. name="checkbox"
  58. >
  59. <Checkbox />
  60. </FormBinder>
  61. </div>
  62. <Button type="primary" style={{width: '242px'}} onClick={this.validateFields}>
  63. </Button>
  64. </div>
  65. </FormBinderWrapper>
  66. <div style={styles.preview}>
  67. <strong>当前表单数据</strong>
  68. <pre>{JSON.stringify(this.state.value, null, 2)}</pre>
  69. </div>
  70. </div>
  71. );
  72. }
  73. }
  74. const styles = {
  75. formItem: {
  76. marginBottom: '20px'
  77. },
  78. formItemLabel: {
  79. },
  80. formItemError: {
  81. marginLeft: '10px',
  82. },
  83. preview: {
  84. border: '1px solid #eee',
  85. marginTop: 20,
  86. padding: 10
  87. }
  88. }
  89. ReactDOM.render(<Login />, mountNode);

注册表单

普通的注册表单,展示多表单元素的组合,用户填写必须的信息才能注册新用户。

FormBinder ICE 表单粘合剂 - 图2

查看源码在线预览

  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
  4. import { Input, Button, Checkbox, Feedback, CascaderSelect, Switch, DatePicker } from '@icedesign/base';
  5. const cityData = [
  6. {
  7. value: "2973",
  8. label: "陕西",
  9. children: [
  10. {
  11. value: "2974",
  12. label: "西安",
  13. children: [
  14. { value: "2975", label: "西安市" },
  15. { value: "2976", label: "高陵县" }
  16. ]
  17. },
  18. {
  19. value: "2980",
  20. label: "铜川",
  21. children: [
  22. { value: "2981", label: "铜川市" },
  23. { value: "2982", label: "宜君县" }
  24. ]
  25. }
  26. ]
  27. },
  28. {
  29. value: "3371",
  30. label: "新疆",
  31. children: [
  32. {
  33. value: "3430",
  34. label: "巴音郭楞蒙古自治州",
  35. children: [
  36. { value: "3431", label: "库尔勒市" },
  37. { value: "3432", label: "和静县" }
  38. ]
  39. }
  40. ]
  41. }
  42. ];
  43. class Register extends Component {
  44. constructor(props) {
  45. super(props);
  46. this.state = {
  47. value: {
  48. email: '',
  49. password: '',
  50. confirmPassword: '',
  51. nickname: '',
  52. birthdate: '',
  53. city: '',
  54. notification: false,
  55. agreement: false,
  56. }
  57. };
  58. }
  59. formChange = value => {
  60. // 说明:
  61. // 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
  62. // 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
  63. this.setState({ value });
  64. };
  65. checkPassword = (rule, values, callback) => {
  66. if (!values) {
  67. callback('请输入正确的密码');
  68. } else if (values.length < 8) {
  69. callback('密码必须大于8位');
  70. } else if (values.length > 16) {
  71. callback('密码必须小于16位');
  72. } else {
  73. callback();
  74. }
  75. };
  76. checkConfirmPassword = (rule, values, callback) => {
  77. if (!values) {
  78. callback('请输入正确的密码');
  79. } else if (values && values !== this.state.value.password) {
  80. callback('两次输入密码不一致');
  81. } else {
  82. callback();
  83. }
  84. };
  85. validateFields = () => {
  86. const { validateFields } = this.refs.form;
  87. validateFields((errors, values) => {
  88. console.log({ errors })
  89. if (!errors) {
  90. Feedback.toast.success('注册成功')
  91. }
  92. });
  93. }
  94. render() {
  95. return (
  96. <div style={styles.container}>
  97. <FormBinderWrapper
  98. value={this.state.value}
  99. onChange={this.formChange}
  100. ref="form"
  101. >
  102. <div style={styles.content}>
  103. <div style={styles.formItem}>
  104. <span style={styles.formItemLabel}>邮箱:</span>
  105. <FormBinder name="email" required type="email" message="请输入正确的邮箱" >
  106. <Input placeholder="ice-admin@alibaba-inc.com" />
  107. </FormBinder>
  108. <FormError style={styles.formItemError} name="email" />
  109. </div>
  110. <div style={styles.formItem}>
  111. <span style={styles.formItemLabel}>昵称:</span>
  112. <FormBinder name="nickname" required message="请输入正确的昵称" >
  113. <Input placeholder="淘小宝" />
  114. </FormBinder>
  115. <FormError style={styles.formItemError} name="nickname" />
  116. </div>
  117. <div style={styles.formItem}>
  118. <span style={styles.formItemLabel}>密码:</span>
  119. <FormBinder name="password" required validator={this.checkPassword}>
  120. <Input htmlType="password" placeholder="输入密码" />
  121. </FormBinder>
  122. <FormError style={styles.formItemError} name="password" />
  123. </div>
  124. <div style={styles.formItem}>
  125. <span style={styles.formItemLabel}>确认密码:</span>
  126. <FormBinder name="confirmPassword" required validator={this.checkConfirmPassword}>
  127. <Input htmlType="password" placeholder="输入确认密码" />
  128. </FormBinder>
  129. <FormError style={styles.formItemError} name="confirmPassword" />
  130. </div>
  131. <div style={styles.formItem}>
  132. <span style={styles.formItemLabel}>出生日期:</span>
  133. <FormBinder name="birthdate" getFieldValue={(date, formatDate) => { return formatDate }}>
  134. <DatePicker formater={["YYYY-MM-DD"]} style={{ width: '200px' }} />
  135. </FormBinder>
  136. </div>
  137. <div style={styles.formItem}>
  138. <span style={styles.formItemLabel}>籍贯地址:</span>
  139. <FormBinder name="city" >
  140. <CascaderSelect dataSource={cityData} style={{ width: '200px' }} />
  141. </FormBinder>
  142. </div>
  143. <div style={styles.formItem}>
  144. <span style={styles.formItemLabel}>开启通知:</span>
  145. <FormBinder name="notification" valuePropName="checked">
  146. <Switch />
  147. </FormBinder>
  148. </div>
  149. <div style={styles.formItem}>
  150. <span style={styles.formItemLabel}>用户协议:</span>
  151. <FormBinder
  152. name="agreement"
  153. valuePropName="checked"
  154. >
  155. <Checkbox />
  156. </FormBinder>
  157. </div>
  158. <Button type="primary" style={{width: '270px'}} onClick={this.validateFields}>
  159. </Button>
  160. </div>
  161. </FormBinderWrapper>
  162. <div style={styles.preview}>
  163. <strong>当前表单数据</strong>
  164. <pre>{JSON.stringify(this.state.value, null, 2)}</pre>
  165. </div>
  166. </div>
  167. );
  168. }
  169. }
  170. const styles = {
  171. formItem: {
  172. marginBottom: '20px',
  173. display: 'flex',
  174. alignItems: 'center',
  175. },
  176. formItemLabel: {
  177. width: '70px',
  178. mariginRight: '10px',
  179. display: 'inline-block',
  180. textAlign: 'right',
  181. },
  182. formItemError: {
  183. marginLeft: '10px',
  184. },
  185. preview: {
  186. border: '1px solid #eee',
  187. marginTop: 20,
  188. padding: 10
  189. }
  190. }
  191. ReactDOM.render(<Register />, mountNode);

时间类组件

时间类组件的 value 类型为 moment 对象,一般在提交服务器前需要预处理

FormBinder ICE 表单粘合剂 - 图3

查看源码在线预览

  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
  4. import { DatePicker, TimePicker, Button } from '@icedesign/base';
  5. const { MonthPicker, RangePicker } = DatePicker;
  6. class Time extends Component {
  7. constructor(props) {
  8. super(props);
  9. this.state = {
  10. value: {
  11. datePicker: '',
  12. dateTimePicker: '',
  13. monthPicker: '',
  14. rangePicker: [],
  15. rangeRimePicker: [],
  16. timePicker: '',
  17. }
  18. };
  19. }
  20. formChange = value => {
  21. // 说明:
  22. // 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
  23. // 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
  24. this.setState({ value });
  25. };
  26. validateFields = () => {
  27. const { validateFields } = this.refs.form;
  28. validateFields((errors, values) => {
  29. console.log(errors, values)
  30. });
  31. }
  32. render() {
  33. const config = {
  34. required: true,
  35. message: "请选择",
  36. getFieldValue: (date, formatDate) => { return formatDate }
  37. }
  38. const style = {
  39. width: '350px',
  40. }
  41. return (
  42. <div style={styles.container}>
  43. <FormBinderWrapper
  44. value={this.state.value}
  45. onChange={this.formChange}
  46. ref="form"
  47. >
  48. <div style={styles.content}>
  49. <div style={styles.formItem}>
  50. <span style={styles.formItemLabel}>日期选择:</span>
  51. <FormBinder name="datePicker" {...config}>
  52. <DatePicker formater={["YYYY-MM-DD"]} style={{...style}} />
  53. </FormBinder>
  54. <FormError style={styles.formItemError} name="datePicker" />
  55. </div>
  56. <div style={styles.formItem}>
  57. <span style={styles.formItemLabel}>日期时间:</span>
  58. <FormBinder name="dateTimePicker" {...config}>
  59. <DatePicker showTime formater={["YYYY-MM-DD"]} style={{...style}} />
  60. </FormBinder>
  61. <FormError style={styles.formItemError} name="dateTimePicker" />
  62. </div>
  63. <div style={styles.formItem}>
  64. <span style={styles.formItemLabel}>月份选择:</span>
  65. <FormBinder name="monthPicker" {...config}>
  66. <MonthPicker formater={["YYYY-MM"]} style={{...style}} />
  67. </FormBinder>
  68. <FormError style={styles.formItemError} name="monthPicker" />
  69. </div>
  70. <div style={styles.formItem}>
  71. <span style={styles.formItemLabel}>区间选择:</span>
  72. <FormBinder name="rangePicker" {...config}>
  73. <RangePicker formater={["YYYY-MM-DD"]} style={{...style}} />
  74. </FormBinder>
  75. <FormError style={styles.formItemError} name="rangePicker" />
  76. </div>
  77. <div style={styles.formItem}>
  78. <span style={styles.formItemLabel}>区间时间:</span>
  79. <FormBinder name="rangeRimePicker" {...config}>
  80. <RangePicker showTime formater={["YYYY-MM-DD"]} />
  81. </FormBinder>
  82. <FormError style={styles.formItemError} name="rangeRimePicker" />
  83. </div>
  84. <div style={styles.formItem}>
  85. <span style={styles.formItemLabel}>时间选择:</span>
  86. <FormBinder name="timePicker" {...config}>
  87. <TimePicker format="HH:mm:ss" style={{...style}} />
  88. </FormBinder>
  89. <FormError style={styles.formItemError} name="timePicker" />
  90. </div>
  91. <Button type="primary" style={{marginLeft: '80px'}} onClick={this.validateFields}>
  92. </Button>
  93. </div>
  94. </FormBinderWrapper>
  95. <div style={styles.preview}>
  96. <strong>当前表单数据</strong>
  97. <pre>{JSON.stringify(this.state.value, null, 2)}</pre>
  98. </div>
  99. </div>
  100. );
  101. }
  102. }
  103. const styles = {
  104. formItem: {
  105. marginBottom: '20px'
  106. },
  107. formItemLabel: {
  108. marginRight: '10px',
  109. },
  110. formItemError: {
  111. marginLeft: '10px',
  112. },
  113. preview: {
  114. border: '1px solid #eee',
  115. marginTop: 20,
  116. padding: 10
  117. }
  118. }
  119. ReactDOM.render(<Time />, mountNode);

重置表单

经过 FormBinder 包裹的 Select 组件,重置值为未选择状态。

FormBinder ICE 表单粘合剂 - 图4

查看源码在线预览

  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
  4. import { Select, Button, Grid } from '@icedesign/base';
  5. const { Row, Col } = Grid;
  6. const dataSource = [];
  7. class Reset extends Component {
  8. constructor(props) {
  9. super(props);
  10. this.state = {
  11. value: {
  12. bu: 'taobao',
  13. },
  14. };
  15. }
  16. formChange = value => {
  17. // 说明:
  18. // 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
  19. // 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
  20. this.setState({ value });
  21. };
  22. handleReset = () => {
  23. this.setState({
  24. value: {
  25. bu: 'taobao',
  26. },
  27. });
  28. };
  29. render() {
  30. return (
  31. <div>
  32. <FormBinderWrapper value={this.state.value} onChange={this.formChange} ref="form">
  33. <div style={styles.content}>
  34. <div style={styles.formItem}>
  35. <span>请选择:</span>
  36. <FormBinder name="bu" required message="请选择">
  37. <Select
  38. dataSource={[
  39. {
  40. value: 'taobao',
  41. label: '淘宝',
  42. },
  43. {
  44. value: 'tmall',
  45. label: '天猫',
  46. },
  47. {
  48. value: 'aliyun',
  49. label: '阿里云',
  50. },
  51. {
  52. value: 'alitrip',
  53. label: '飞猪',
  54. },
  55. ]}
  56. placeholder="请选择"
  57. autoWidth={false}
  58. />
  59. </FormBinder>
  60. <FormError name="bu" />
  61. </div>
  62. <Button type="primary" onClick={this.handleReset} style={styles.resetButton}>重 置</Button>
  63. </div>
  64. </FormBinderWrapper>
  65. <div style={styles.preview}>
  66. <strong>当前表单数据:</strong>
  67. <pre>
  68. {JSON.stringify(this.state.value, null, 2)}
  69. </pre>
  70. </div>
  71. </div>
  72. );
  73. }
  74. }
  75. const styles = {
  76. formItem: {
  77. display: 'flex',
  78. alignItems: 'center',
  79. marginBottom: '20px',
  80. },
  81. resetButton: {
  82. marginLeft: '56px',
  83. },
  84. preview: {
  85. border: '1px solid #eee',
  86. margin: '20px 0',
  87. padding: '10px'
  88. }
  89. }
  90. ReactDOM.render(<Reset />, mountNode);

分步校验表单

在表单检验中,可以分步骤对表单进行校验,先校验一部分表单域通过后在检验另外一部分表单

FormBinder ICE 表单粘合剂 - 图5

查看源码在线预览

  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
  4. import { Input, Button } from '@icedesign/base';
  5. class App extends Component {
  6. constructor(props) {
  7. super(props);
  8. this.state = {
  9. value: {
  10. email: '',
  11. name: '',
  12. password: '',
  13. }
  14. };
  15. }
  16. formChange = value => {
  17. // 说明:
  18. // 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
  19. // 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
  20. this.setState({ value });
  21. };
  22. validateFields = (fieldnames = []) => {
  23. const cb = (errors, values) => {
  24. console.log('validateFields:', errors, values)
  25. }
  26. const { validateFields } = this.refs.form;
  27. if (fieldnames.length) {
  28. validateFields(fieldnames, cb);
  29. } else {
  30. validateFields(cb);
  31. }
  32. }
  33. render() {
  34. return (
  35. <div>
  36. <FormBinderWrapper
  37. value={this.state.value}
  38. onChange={this.formChange}
  39. ref="form"
  40. >
  41. <div>
  42. <div style={styles.formItem}>
  43. <span style={styles.formLabel}>名称:</span>
  44. <FormBinder name="name" required message="请输入正确的名称" >
  45. <Input placeholder="淘小宝" />
  46. </FormBinder>
  47. <FormError style={styles.formError} name="name" />
  48. </div>
  49. <div style={styles.formItem}>
  50. <span style={styles.formLabel}>邮箱:</span>
  51. <FormBinder name="email" type="email" required message="请输入正确的邮箱">
  52. <Input placeholder="ice-admin@alibaba-inc.com" />
  53. </FormBinder>
  54. <FormError style={styles.formError} name="email" />
  55. </div>
  56. <div style={styles.formItem}>
  57. <span style={styles.formLabel}>设置密码:</span>
  58. <FormBinder name="password" required message="请输入新密码" >
  59. <Input htmlType="password" placeholder="设置新密码" />
  60. </FormBinder>
  61. <FormError style={styles.formError} name="password" />
  62. </div>
  63. </div>
  64. </FormBinderWrapper>
  65. <div style={{marginTop: 20}}>
  66. <Button type="primary" style={{marginRight: 10}} onClick={() => this.validateFields(['name', 'email'])}>
  67. 先校验名称和邮箱
  68. </Button>
  69. <Button type="secondary" onClick={this.validateFields}>
  70. 校验整个表单
  71. </Button>
  72. </div>
  73. <div style={styles.preview}>
  74. <strong>当前表单数据</strong>
  75. <pre>{JSON.stringify(this.state.value, null, 2)}</pre>
  76. </div>
  77. </div>
  78. );
  79. }
  80. }
  81. const styles = {
  82. formItem: {
  83. marginBottom: '20px'
  84. },
  85. formLabel: {
  86. width: '70px',
  87. marginRight: '10px',
  88. textAlign: 'right',
  89. display: 'inline-block'
  90. },
  91. formError: {
  92. marginLeft: '10px',
  93. },
  94. preview: {
  95. border: '1px solid #eee',
  96. marginTop: 20,
  97. padding: 10
  98. }
  99. }
  100. ReactDOM.render(<App />, mountNode);

自定义校验

可以使用 validator 自定义校验,根据不同情况执行不同的校验规则

FormBinder ICE 表单粘合剂 - 图6

查看源码在线预览

  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
  4. import { Select, Button, Grid, Input } from '@icedesign/base';
  5. const { Row, Col } = Grid;
  6. class CustomValidator extends Component {
  7. constructor(props) {
  8. super(props);
  9. this.state = {
  10. value: { input: '' },
  11. };
  12. }
  13. // 说明:
  14. // 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
  15. // 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
  16. formChange = (value) => {
  17. this.setState({ value })
  18. }
  19. // 通过 validator 自定义校验规则,更多用法参考 https://github.com/yiminghe/async-validator#usage
  20. inputValidator = (rule, value, callback) => {
  21. const errors = [];
  22. console.log(value)
  23. if (!value) {
  24. callback('输入不能为空');
  25. } else if (value.length < 8) {
  26. callback('输入长度必须大于 8 位');
  27. } else if (value.length > 16) {
  28. callback('输入长度必须小于 16 位');
  29. } else {
  30. callback();
  31. }
  32. };
  33. render() {
  34. return (
  35. <div>
  36. <FormBinderWrapper
  37. value={this.state.value}
  38. onChange={this.formChange}
  39. ref="form"
  40. >
  41. <div style={styles.formItem}>
  42. <span style={styles.formLabel}>自定义校验:</span>
  43. <FormBinder name="input" required validator={this.inputValidator} >
  44. <Input placeholder="请输入"/>
  45. </FormBinder>
  46. <FormError name="input" style={styles.formError} />
  47. </div>
  48. </FormBinderWrapper>
  49. <p style={styles.desc}>输入不能为空,且长度必须大于8位小于16位</p>
  50. <div style={styles.preview}>
  51. <strong>当前表单数据:</strong>
  52. <pre>
  53. {JSON.stringify(this.state.value, null, 2)}
  54. </pre>
  55. </div>
  56. </div>
  57. );
  58. }
  59. }
  60. const styles = {
  61. formItem: {
  62. dispaly: 'flex',
  63. alignItems: 'center',
  64. },
  65. formLabel: {
  66. marginRight: '10px'
  67. },
  68. formError: {
  69. marginLeft: '10px',
  70. },
  71. desc: {
  72. margin: '5px 0 20px 94px',
  73. color: '#999',
  74. fontSize: '12px'
  75. },
  76. preview: {
  77. border: '1px solid #eee',
  78. margin: '20px 0',
  79. padding: '10px'
  80. }
  81. }
  82. ReactDOM.render(<CustomValidator />, mountNode);

自定义 valuePropName

有时候自定义或第三方的表单组件的取值属性不是 value,可以通过 valuePropName 来进行修改,也可以通过 setFieldValue 和 getFieldValue 进行转换

FormBinder ICE 表单粘合剂 - 图7

查看源码在线预览

  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
  4. import { Checkbox, Switch } from '@icedesign/base';
  5. class App extends Component {
  6. state = {
  7. value: {
  8. checkbox: true,
  9. switch: 1
  10. }
  11. };
  12. formChange = (value) => {
  13. // 说明:
  14. // 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
  15. // 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
  16. this.setState({ value })
  17. }
  18. render() {
  19. return (
  20. <div>
  21. <FormBinderWrapper
  22. value={this.state.value}
  23. onChange={this.formChange}
  24. >
  25. <div style={styles.content}>
  26. <div style={styles.formItem}>
  27. <span>复选框:</span>
  28. <FormBinder name="checkbox" valuePropName="checked">
  29. <Checkbox />
  30. </FormBinder>
  31. </div>
  32. <div style={styles.formItem}>
  33. <span>开关:</span>
  34. <FormBinder
  35. name="switch"
  36. valuePropName="checked" // Switch 接收的属性是 `checked`
  37. setFieldValue={(selected) => { return selected === 1 }} // 转换为 boolean 传给 switch
  38. getFieldValue={(checked) => { return checked ? 1 : 0 }} // 返回值转换为 number 给表单值
  39. >
  40. <Switch size="small" />
  41. </FormBinder>
  42. </div>
  43. </div>
  44. </FormBinderWrapper>
  45. <div style={styles.preview}>
  46. <pre>{JSON.stringify(this.state, null, 2)}</pre>
  47. </div>
  48. </div>
  49. )
  50. }
  51. }
  52. const styles = {
  53. formItem: {
  54. display: 'flex',
  55. alignItems: 'center',
  56. marginBottom: '20px'
  57. },
  58. preview: {
  59. border: '1px solid #eee',
  60. marginTop: 20,
  61. padding: 10
  62. }
  63. }
  64. ReactDOM.render(<App />, mountNode);

动态增加、减少表单项

演示 Input 组件数据交互和循环数组数据交互,数组表单用法,动态增加、减少表单项。

FormBinder ICE 表单粘合剂 - 图8

查看源码在线预览

  1. import React, { Component } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import { FormBinderWrapper, FormBinder, FormError } from '@icedesign/form-binder';
  4. import { Input, Button, Grid, DatePicker } from '@icedesign/base';
  5. const { Row, Col } = Grid;
  6. class App extends Component {
  7. constructor(props) {
  8. super(props);
  9. this.state = {
  10. value: {
  11. items: [{}]
  12. }
  13. };
  14. }
  15. addItem = () => {
  16. this.state.value.items.push({});
  17. this.setState({ value: this.state.value });
  18. };
  19. formChange = value => {
  20. // 说明:
  21. // 1. 表单是双向通行的,所有表单的响应数据都会同步更新 value
  22. // 2. 这里 setState 只是为了实时展示当前表单数据的演示使用
  23. this.setState({ value });
  24. };
  25. changeItem = () => {
  26. let items = this.state.value.items;
  27. items[0].aaa = '有趣';
  28. this.setState({
  29. value: {
  30. ...this.state.value,
  31. items: items
  32. }
  33. });
  34. };
  35. removeItem = (index) => {
  36. this.state.value.items.splice(index, 1);
  37. this.setState({
  38. value: this.state.value
  39. });
  40. }
  41. validateAllFormField = () => {
  42. this.refs.form.validateFields((errors, values) => {
  43. console.log('errors', errors, 'values', values);
  44. });
  45. };
  46. render() {
  47. return (
  48. <div>
  49. <FormBinderWrapper
  50. value={this.state.value}
  51. onChange={this.formChange}
  52. ref="form"
  53. >
  54. <ArticleList
  55. items={this.state.value.items}
  56. addItem={this.addItem}
  57. removeItem={this.removeItem}
  58. validateAllFormField={this.validateAllFormField}
  59. />
  60. </FormBinderWrapper>
  61. <div style={styles.preview}>
  62. <strong>当前表单数据:</strong>
  63. <pre>{JSON.stringify(this.state.value, null, 2)}</pre>
  64. </div>
  65. </div>
  66. );
  67. }
  68. }
  69. class ArticleList extends Component {
  70. render() {
  71. return (
  72. <div>
  73. {this.props.items.map((item, index) => {
  74. return (
  75. <Row key={index} style={styles.row}>
  76. <Col>
  77. <span>文章名称:</span>
  78. <FormBinder required message="文章名称必填" name={`items[${index}].name`} >
  79. <Input />
  80. </FormBinder>
  81. <FormError name={`items[${index}].name`} style={styles.formError} />
  82. </Col>
  83. <Col>
  84. <span>文章地址:</span>
  85. <FormBinder name={`items[${index}].url`} type="url" required message="请输入正确的 URL 地址" >
  86. <Input />
  87. </FormBinder>
  88. <FormError name={`items[${index}].url`} style={styles.formError} />
  89. </Col>
  90. <Col>
  91. <Button type="secondary" onClick={this.props.removeItem.bind(this, index)}>删除</Button>
  92. </Col>
  93. </Row>
  94. );
  95. })}
  96. <div style={styles.buttons}>
  97. <Button type="secondary" onClick={this.props.addItem}>新 增</Button>
  98. <Button type="primary" style={{marginLeft: 10}} onClick={this.props.validateAllFormField}>
  99. </Button>
  100. </div>
  101. </div>
  102. );
  103. }
  104. }
  105. const styles = {
  106. row: {
  107. marginBottom: '20px',
  108. },
  109. formError: {
  110. display: 'block',
  111. marginTop: '10px',
  112. marginLeft: '70px',
  113. },
  114. buttons: {
  115. margin: '20px 0 0 86px',
  116. },
  117. preview: {
  118. border: '1px solid #eee',
  119. marginTop: 20,
  120. padding: 10
  121. }
  122. }
  123. ReactDOM.render(<App />, mountNode);

相关区块

FormBinder ICE 表单粘合剂 - 图9

暂无相关区块