Form表单

具有数据收集、校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。

何时使用

  • 用于创建一个实体或收集信息。

  • 需要对输入的数据类型进行校验时。

表单

我们为 form 提供了以下三种排列方式:

  • 水平排列:标签和表单控件水平排列;(默认)

  • 垂直排列:标签和表单控件上下垂直排列;

  • 行内排列:表单项水平行内排列。

表单域

表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等。

这里我们封装了表单域 <Form.Item />

  1. <Form.Item {...props}>{children}</Form.Item>

代码演示

Form 表单 - 图1

水平登录栏

水平登录栏,常用在顶部导航栏中。

  1. import { Form, Icon, Input, Button } from 'antd';
  2. function hasErrors(fieldsError) {
  3. return Object.keys(fieldsError).some(field => fieldsError[field]);
  4. }
  5. class HorizontalLoginForm extends React.Component {
  6. componentDidMount() {
  7. // To disabled submit button at the beginning.
  8. this.props.form.validateFields();
  9. }
  10. handleSubmit = e => {
  11. e.preventDefault();
  12. this.props.form.validateFields((err, values) => {
  13. if (!err) {
  14. console.log('Received values of form: ', values);
  15. }
  16. });
  17. };
  18. render() {
  19. const { getFieldDecorator, getFieldsError, getFieldError, isFieldTouched } = this.props.form;
  20. // Only show error after a field is touched.
  21. const usernameError = isFieldTouched('username') && getFieldError('username');
  22. const passwordError = isFieldTouched('password') && getFieldError('password');
  23. return (
  24. <Form layout="inline" onSubmit={this.handleSubmit}>
  25. <Form.Item validateStatus={usernameError ? 'error' : ''} help={usernameError || ''}>
  26. {getFieldDecorator('username', {
  27. rules: [{ required: true, message: 'Please input your username!' }],
  28. })(
  29. <Input
  30. prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
  31. placeholder="Username"
  32. />,
  33. )}
  34. </Form.Item>
  35. <Form.Item validateStatus={passwordError ? 'error' : ''} help={passwordError || ''}>
  36. {getFieldDecorator('password', {
  37. rules: [{ required: true, message: 'Please input your Password!' }],
  38. })(
  39. <Input
  40. prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
  41. type="password"
  42. placeholder="Password"
  43. />,
  44. )}
  45. </Form.Item>
  46. <Form.Item>
  47. <Button type="primary" htmlType="submit" disabled={hasErrors(getFieldsError())}>
  48. Log in
  49. </Button>
  50. </Form.Item>
  51. </Form>
  52. );
  53. }
  54. }
  55. const WrappedHorizontalLoginForm = Form.create({ name: 'horizontal_login' })(HorizontalLoginForm);
  56. ReactDOM.render(<WrappedHorizontalLoginForm />, mountNode);

Form 表单 - 图2

登录框

普通的登录框,可以容纳更多的元素。

  1. import { Form, Icon, Input, Button, Checkbox } from 'antd';
  2. class NormalLoginForm extends React.Component {
  3. handleSubmit = e => {
  4. e.preventDefault();
  5. this.props.form.validateFields((err, values) => {
  6. if (!err) {
  7. console.log('Received values of form: ', values);
  8. }
  9. });
  10. };
  11. render() {
  12. const { getFieldDecorator } = this.props.form;
  13. return (
  14. <Form onSubmit={this.handleSubmit} className="login-form">
  15. <Form.Item>
  16. {getFieldDecorator('username', {
  17. rules: [{ required: true, message: 'Please input your username!' }],
  18. })(
  19. <Input
  20. prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
  21. placeholder="Username"
  22. />,
  23. )}
  24. </Form.Item>
  25. <Form.Item>
  26. {getFieldDecorator('password', {
  27. rules: [{ required: true, message: 'Please input your Password!' }],
  28. })(
  29. <Input
  30. prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
  31. type="password"
  32. placeholder="Password"
  33. />,
  34. )}
  35. </Form.Item>
  36. <Form.Item>
  37. {getFieldDecorator('remember', {
  38. valuePropName: 'checked',
  39. initialValue: true,
  40. })(<Checkbox>Remember me</Checkbox>)}
  41. <a className="login-form-forgot" href="">
  42. Forgot password
  43. </a>
  44. <Button type="primary" htmlType="submit" className="login-form-button">
  45. Log in
  46. </Button>
  47. Or <a href="">register now!</a>
  48. </Form.Item>
  49. </Form>
  50. );
  51. }
  52. }
  53. const WrappedNormalLoginForm = Form.create({ name: 'normal_login' })(NormalLoginForm);
  54. ReactDOM.render(<WrappedNormalLoginForm />, mountNode);
  1. #components-form-demo-normal-login .login-form {
  2. max-width: 300px;
  3. }
  4. #components-form-demo-normal-login .login-form-forgot {
  5. float: right;
  6. }
  7. #components-form-demo-normal-login .login-form-button {
  8. width: 100%;
  9. }

Form 表单 - 图3

注册新用户

用户填写必须的信息以注册新用户。

  1. import {
  2. Form,
  3. Input,
  4. Tooltip,
  5. Icon,
  6. Cascader,
  7. Select,
  8. Row,
  9. Col,
  10. Checkbox,
  11. Button,
  12. AutoComplete,
  13. } from 'antd';
  14. const { Option } = Select;
  15. const AutoCompleteOption = AutoComplete.Option;
  16. const residences = [
  17. {
  18. value: 'zhejiang',
  19. label: 'Zhejiang',
  20. children: [
  21. {
  22. value: 'hangzhou',
  23. label: 'Hangzhou',
  24. children: [
  25. {
  26. value: 'xihu',
  27. label: 'West Lake',
  28. },
  29. ],
  30. },
  31. ],
  32. },
  33. {
  34. value: 'jiangsu',
  35. label: 'Jiangsu',
  36. children: [
  37. {
  38. value: 'nanjing',
  39. label: 'Nanjing',
  40. children: [
  41. {
  42. value: 'zhonghuamen',
  43. label: 'Zhong Hua Men',
  44. },
  45. ],
  46. },
  47. ],
  48. },
  49. ];
  50. class RegistrationForm extends React.Component {
  51. state = {
  52. confirmDirty: false,
  53. autoCompleteResult: [],
  54. };
  55. handleSubmit = e => {
  56. e.preventDefault();
  57. this.props.form.validateFieldsAndScroll((err, values) => {
  58. if (!err) {
  59. console.log('Received values of form: ', values);
  60. }
  61. });
  62. };
  63. handleConfirmBlur = e => {
  64. const value = e.target.value;
  65. this.setState({ confirmDirty: this.state.confirmDirty || !!value });
  66. };
  67. compareToFirstPassword = (rule, value, callback) => {
  68. const form = this.props.form;
  69. if (value && value !== form.getFieldValue('password')) {
  70. callback('Two passwords that you enter is inconsistent!');
  71. } else {
  72. callback();
  73. }
  74. };
  75. validateToNextPassword = (rule, value, callback) => {
  76. const form = this.props.form;
  77. if (value && this.state.confirmDirty) {
  78. form.validateFields(['confirm'], { force: true });
  79. }
  80. callback();
  81. };
  82. handleWebsiteChange = value => {
  83. let autoCompleteResult;
  84. if (!value) {
  85. autoCompleteResult = [];
  86. } else {
  87. autoCompleteResult = ['.com', '.org', '.net'].map(domain => `${value}${domain}`);
  88. }
  89. this.setState({ autoCompleteResult });
  90. };
  91. render() {
  92. const { getFieldDecorator } = this.props.form;
  93. const { autoCompleteResult } = this.state;
  94. const formItemLayout = {
  95. labelCol: {
  96. xs: { span: 24 },
  97. sm: { span: 8 },
  98. },
  99. wrapperCol: {
  100. xs: { span: 24 },
  101. sm: { span: 16 },
  102. },
  103. };
  104. const tailFormItemLayout = {
  105. wrapperCol: {
  106. xs: {
  107. span: 24,
  108. offset: 0,
  109. },
  110. sm: {
  111. span: 16,
  112. offset: 8,
  113. },
  114. },
  115. };
  116. const prefixSelector = getFieldDecorator('prefix', {
  117. initialValue: '86',
  118. })(
  119. <Select style={{ width: 70 }}>
  120. <Option value="86">+86</Option>
  121. <Option value="87">+87</Option>
  122. </Select>,
  123. );
  124. const websiteOptions = autoCompleteResult.map(website => (
  125. <AutoCompleteOption key={website}>{website}</AutoCompleteOption>
  126. ));
  127. return (
  128. <Form {...formItemLayout} onSubmit={this.handleSubmit}>
  129. <Form.Item label="E-mail">
  130. {getFieldDecorator('email', {
  131. rules: [
  132. {
  133. type: 'email',
  134. message: 'The input is not valid E-mail!',
  135. },
  136. {
  137. required: true,
  138. message: 'Please input your E-mail!',
  139. },
  140. ],
  141. })(<Input />)}
  142. </Form.Item>
  143. <Form.Item label="Password" hasFeedback>
  144. {getFieldDecorator('password', {
  145. rules: [
  146. {
  147. required: true,
  148. message: 'Please input your password!',
  149. },
  150. {
  151. validator: this.validateToNextPassword,
  152. },
  153. ],
  154. })(<Input.Password />)}
  155. </Form.Item>
  156. <Form.Item label="Confirm Password" hasFeedback>
  157. {getFieldDecorator('confirm', {
  158. rules: [
  159. {
  160. required: true,
  161. message: 'Please confirm your password!',
  162. },
  163. {
  164. validator: this.compareToFirstPassword,
  165. },
  166. ],
  167. })(<Input.Password onBlur={this.handleConfirmBlur} />)}
  168. </Form.Item>
  169. <Form.Item
  170. label={
  171. <span>
  172. Nickname&nbsp;
  173. <Tooltip title="What do you want others to call you?">
  174. <Icon type="question-circle-o" />
  175. </Tooltip>
  176. </span>
  177. }
  178. >
  179. {getFieldDecorator('nickname', {
  180. rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
  181. })(<Input />)}
  182. </Form.Item>
  183. <Form.Item label="Habitual Residence">
  184. {getFieldDecorator('residence', {
  185. initialValue: ['zhejiang', 'hangzhou', 'xihu'],
  186. rules: [
  187. { type: 'array', required: true, message: 'Please select your habitual residence!' },
  188. ],
  189. })(<Cascader options={residences} />)}
  190. </Form.Item>
  191. <Form.Item label="Phone Number">
  192. {getFieldDecorator('phone', {
  193. rules: [{ required: true, message: 'Please input your phone number!' }],
  194. })(<Input addonBefore={prefixSelector} style={{ width: '100%' }} />)}
  195. </Form.Item>
  196. <Form.Item label="Website">
  197. {getFieldDecorator('website', {
  198. rules: [{ required: true, message: 'Please input website!' }],
  199. })(
  200. <AutoComplete
  201. dataSource={websiteOptions}
  202. onChange={this.handleWebsiteChange}
  203. placeholder="website"
  204. >
  205. <Input />
  206. </AutoComplete>,
  207. )}
  208. </Form.Item>
  209. <Form.Item label="Captcha" extra="We must make sure that your are a human.">
  210. <Row gutter={8}>
  211. <Col span={12}>
  212. {getFieldDecorator('captcha', {
  213. rules: [{ required: true, message: 'Please input the captcha you got!' }],
  214. })(<Input />)}
  215. </Col>
  216. <Col span={12}>
  217. <Button>Get captcha</Button>
  218. </Col>
  219. </Row>
  220. </Form.Item>
  221. <Form.Item {...tailFormItemLayout}>
  222. {getFieldDecorator('agreement', {
  223. valuePropName: 'checked',
  224. })(
  225. <Checkbox>
  226. I have read the <a href="">agreement</a>
  227. </Checkbox>,
  228. )}
  229. </Form.Item>
  230. <Form.Item {...tailFormItemLayout}>
  231. <Button type="primary" htmlType="submit">
  232. Register
  233. </Button>
  234. </Form.Item>
  235. </Form>
  236. );
  237. }
  238. }
  239. const WrappedRegistrationForm = Form.create({ name: 'register' })(RegistrationForm);
  240. ReactDOM.render(<WrappedRegistrationForm />, mountNode);

Form 表单 - 图4

高级搜索

三列栅格式的表单排列方式,常用于数据表格的高级搜索。

有部分定制的样式代码,由于输入标签长度不确定,需要根据具体情况自行调整。

  1. import { Form, Row, Col, Input, Button, Icon } from 'antd';
  2. class AdvancedSearchForm extends React.Component {
  3. state = {
  4. expand: false,
  5. };
  6. // To generate mock Form.Item
  7. getFields() {
  8. const count = this.state.expand ? 10 : 6;
  9. const { getFieldDecorator } = this.props.form;
  10. const children = [];
  11. for (let i = 0; i < 10; i++) {
  12. children.push(
  13. <Col span={8} key={i} style={{ display: i < count ? 'block' : 'none' }}>
  14. <Form.Item label={`Field ${i}`}>
  15. {getFieldDecorator(`field-${i}`, {
  16. rules: [
  17. {
  18. required: true,
  19. message: 'Input something!',
  20. },
  21. ],
  22. })(<Input placeholder="placeholder" />)}
  23. </Form.Item>
  24. </Col>,
  25. );
  26. }
  27. return children;
  28. }
  29. handleSearch = e => {
  30. e.preventDefault();
  31. this.props.form.validateFields((err, values) => {
  32. console.log('Received values of form: ', values);
  33. });
  34. };
  35. handleReset = () => {
  36. this.props.form.resetFields();
  37. };
  38. toggle = () => {
  39. const { expand } = this.state;
  40. this.setState({ expand: !expand });
  41. };
  42. render() {
  43. return (
  44. <Form className="ant-advanced-search-form" onSubmit={this.handleSearch}>
  45. <Row gutter={24}>{this.getFields()}</Row>
  46. <Row>
  47. <Col span={24} style={{ textAlign: 'right' }}>
  48. <Button type="primary" htmlType="submit">
  49. Search
  50. </Button>
  51. <Button style={{ marginLeft: 8 }} onClick={this.handleReset}>
  52. Clear
  53. </Button>
  54. <a style={{ marginLeft: 8, fontSize: 12 }} onClick={this.toggle}>
  55. Collapse <Icon type={this.state.expand ? 'up' : 'down'} />
  56. </a>
  57. </Col>
  58. </Row>
  59. </Form>
  60. );
  61. }
  62. }
  63. const WrappedAdvancedSearchForm = Form.create({ name: 'advanced_search' })(AdvancedSearchForm);
  64. ReactDOM.render(
  65. <div>
  66. <WrappedAdvancedSearchForm />
  67. <div className="search-result-list">Search Result List</div>
  68. </div>,
  69. mountNode,
  70. );
  1. .ant-advanced-search-form {
  2. padding: 24px;
  3. background: #fbfbfb;
  4. border: 1px solid #d9d9d9;
  5. border-radius: 6px;
  6. }
  7. .ant-advanced-search-form .ant-form-item {
  8. display: flex;
  9. }
  10. .ant-advanced-search-form .ant-form-item-control-wrapper {
  11. flex: 1;
  12. }

Form 表单 - 图5

弹出层中的新建表单

当用户访问一个展示了某个列表的页面,想新建一项但又不想跳转页面时,可以用 Modal 弹出一个表单,用户填写必要信息后创建新的项。

  1. import { Button, Modal, Form, Input, Radio } from 'antd';
  2. const CollectionCreateForm = Form.create({ name: 'form_in_modal' })(
  3. // eslint-disable-next-line
  4. class extends React.Component {
  5. render() {
  6. const { visible, onCancel, onCreate, form } = this.props;
  7. const { getFieldDecorator } = form;
  8. return (
  9. <Modal
  10. visible={visible}
  11. title="Create a new collection"
  12. okText="Create"
  13. onCancel={onCancel}
  14. onOk={onCreate}
  15. >
  16. <Form layout="vertical">
  17. <Form.Item label="Title">
  18. {getFieldDecorator('title', {
  19. rules: [{ required: true, message: 'Please input the title of collection!' }],
  20. })(<Input />)}
  21. </Form.Item>
  22. <Form.Item label="Description">
  23. {getFieldDecorator('description')(<Input type="textarea" />)}
  24. </Form.Item>
  25. <Form.Item className="collection-create-form_last-form-item">
  26. {getFieldDecorator('modifier', {
  27. initialValue: 'public',
  28. })(
  29. <Radio.Group>
  30. <Radio value="public">Public</Radio>
  31. <Radio value="private">Private</Radio>
  32. </Radio.Group>,
  33. )}
  34. </Form.Item>
  35. </Form>
  36. </Modal>
  37. );
  38. }
  39. },
  40. );
  41. class CollectionsPage extends React.Component {
  42. state = {
  43. visible: false,
  44. };
  45. showModal = () => {
  46. this.setState({ visible: true });
  47. };
  48. handleCancel = () => {
  49. this.setState({ visible: false });
  50. };
  51. handleCreate = () => {
  52. const form = this.formRef.props.form;
  53. form.validateFields((err, values) => {
  54. if (err) {
  55. return;
  56. }
  57. console.log('Received values of form: ', values);
  58. form.resetFields();
  59. this.setState({ visible: false });
  60. });
  61. };
  62. saveFormRef = formRef => {
  63. this.formRef = formRef;
  64. };
  65. render() {
  66. return (
  67. <div>
  68. <Button type="primary" onClick={this.showModal}>
  69. New Collection
  70. </Button>
  71. <CollectionCreateForm
  72. wrappedComponentRef={this.saveFormRef}
  73. visible={this.state.visible}
  74. onCancel={this.handleCancel}
  75. onCreate={this.handleCreate}
  76. />
  77. </div>
  78. );
  79. }
  80. }
  81. ReactDOM.render(<CollectionsPage />, mountNode);
  1. .collection-create-form_last-form-item {
  2. margin-bottom: 0;
  3. }

Form 表单 - 图6

动态增减表单项

动态增加、减少表单项。

  1. import { Form, Input, Icon, Button } from 'antd';
  2. let id = 0;
  3. class DynamicFieldSet extends React.Component {
  4. remove = k => {
  5. const { form } = this.props;
  6. // can use data-binding to get
  7. const keys = form.getFieldValue('keys');
  8. // We need at least one passenger
  9. if (keys.length === 1) {
  10. return;
  11. }
  12. // can use data-binding to set
  13. form.setFieldsValue({
  14. keys: keys.filter(key => key !== k),
  15. });
  16. };
  17. add = () => {
  18. const { form } = this.props;
  19. // can use data-binding to get
  20. const keys = form.getFieldValue('keys');
  21. const nextKeys = keys.concat(id++);
  22. // can use data-binding to set
  23. // important! notify form to detect changes
  24. form.setFieldsValue({
  25. keys: nextKeys,
  26. });
  27. };
  28. handleSubmit = e => {
  29. e.preventDefault();
  30. this.props.form.validateFields((err, values) => {
  31. if (!err) {
  32. const { keys, names } = values;
  33. console.log('Received values of form: ', values);
  34. console.log('Merged values:', keys.map(key => names[key]));
  35. }
  36. });
  37. };
  38. render() {
  39. const { getFieldDecorator, getFieldValue } = this.props.form;
  40. const formItemLayout = {
  41. labelCol: {
  42. xs: { span: 24 },
  43. sm: { span: 4 },
  44. },
  45. wrapperCol: {
  46. xs: { span: 24 },
  47. sm: { span: 20 },
  48. },
  49. };
  50. const formItemLayoutWithOutLabel = {
  51. wrapperCol: {
  52. xs: { span: 24, offset: 0 },
  53. sm: { span: 20, offset: 4 },
  54. },
  55. };
  56. getFieldDecorator('keys', { initialValue: [] });
  57. const keys = getFieldValue('keys');
  58. const formItems = keys.map((k, index) => (
  59. <Form.Item
  60. {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
  61. label={index === 0 ? 'Passengers' : ''}
  62. required={false}
  63. key={k}
  64. >
  65. {getFieldDecorator(`names[${k}]`, {
  66. validateTrigger: ['onChange', 'onBlur'],
  67. rules: [
  68. {
  69. required: true,
  70. whitespace: true,
  71. message: "Please input passenger's name or delete this field.",
  72. },
  73. ],
  74. })(<Input placeholder="passenger name" style={{ width: '60%', marginRight: 8 }} />)}
  75. {keys.length > 1 ? (
  76. <Icon
  77. className="dynamic-delete-button"
  78. type="minus-circle-o"
  79. onClick={() => this.remove(k)}
  80. />
  81. ) : null}
  82. </Form.Item>
  83. ));
  84. return (
  85. <Form onSubmit={this.handleSubmit}>
  86. {formItems}
  87. <Form.Item {...formItemLayoutWithOutLabel}>
  88. <Button type="dashed" onClick={this.add} style={{ width: '60%' }}>
  89. <Icon type="plus" /> Add field
  90. </Button>
  91. </Form.Item>
  92. <Form.Item {...formItemLayoutWithOutLabel}>
  93. <Button type="primary" htmlType="submit">
  94. Submit
  95. </Button>
  96. </Form.Item>
  97. </Form>
  98. );
  99. }
  100. }
  101. const WrappedDynamicFieldSet = Form.create({ name: 'dynamic_form_item' })(DynamicFieldSet);
  102. ReactDOM.render(<WrappedDynamicFieldSet />, mountNode);
  1. .dynamic-delete-button {
  2. cursor: pointer;
  3. position: relative;
  4. top: 4px;
  5. font-size: 24px;
  6. color: #999;
  7. transition: all 0.3s;
  8. }
  9. .dynamic-delete-button:hover {
  10. color: #777;
  11. }
  12. .dynamic-delete-button[disabled] {
  13. cursor: not-allowed;
  14. opacity: 0.5;
  15. }

Form 表单 - 图7

时间类控件

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

  1. import { Form, DatePicker, TimePicker, Button } from 'antd';
  2. const { MonthPicker, RangePicker } = DatePicker;
  3. class TimeRelatedForm extends React.Component {
  4. handleSubmit = e => {
  5. e.preventDefault();
  6. this.props.form.validateFields((err, fieldsValue) => {
  7. if (err) {
  8. return;
  9. }
  10. // Should format date value before submit.
  11. const rangeValue = fieldsValue['range-picker'];
  12. const rangeTimeValue = fieldsValue['range-time-picker'];
  13. const values = {
  14. ...fieldsValue,
  15. 'date-picker': fieldsValue['date-picker'].format('YYYY-MM-DD'),
  16. 'date-time-picker': fieldsValue['date-time-picker'].format('YYYY-MM-DD HH:mm:ss'),
  17. 'month-picker': fieldsValue['month-picker'].format('YYYY-MM'),
  18. 'range-picker': [rangeValue[0].format('YYYY-MM-DD'), rangeValue[1].format('YYYY-MM-DD')],
  19. 'range-time-picker': [
  20. rangeTimeValue[0].format('YYYY-MM-DD HH:mm:ss'),
  21. rangeTimeValue[1].format('YYYY-MM-DD HH:mm:ss'),
  22. ],
  23. 'time-picker': fieldsValue['time-picker'].format('HH:mm:ss'),
  24. };
  25. console.log('Received values of form: ', values);
  26. });
  27. };
  28. render() {
  29. const { getFieldDecorator } = this.props.form;
  30. const formItemLayout = {
  31. labelCol: {
  32. xs: { span: 24 },
  33. sm: { span: 8 },
  34. },
  35. wrapperCol: {
  36. xs: { span: 24 },
  37. sm: { span: 16 },
  38. },
  39. };
  40. const config = {
  41. rules: [{ type: 'object', required: true, message: 'Please select time!' }],
  42. };
  43. const rangeConfig = {
  44. rules: [{ type: 'array', required: true, message: 'Please select time!' }],
  45. };
  46. return (
  47. <Form {...formItemLayout} onSubmit={this.handleSubmit}>
  48. <Form.Item label="DatePicker">
  49. {getFieldDecorator('date-picker', config)(<DatePicker />)}
  50. </Form.Item>
  51. <Form.Item label="DatePicker[showTime]">
  52. {getFieldDecorator('date-time-picker', config)(
  53. <DatePicker showTime format="YYYY-MM-DD HH:mm:ss" />,
  54. )}
  55. </Form.Item>
  56. <Form.Item label="MonthPicker">
  57. {getFieldDecorator('month-picker', config)(<MonthPicker />)}
  58. </Form.Item>
  59. <Form.Item label="RangePicker">
  60. {getFieldDecorator('range-picker', rangeConfig)(<RangePicker />)}
  61. </Form.Item>
  62. <Form.Item label="RangePicker[showTime]">
  63. {getFieldDecorator('range-time-picker', rangeConfig)(
  64. <RangePicker showTime format="YYYY-MM-DD HH:mm:ss" />,
  65. )}
  66. </Form.Item>
  67. <Form.Item label="TimePicker">
  68. {getFieldDecorator('time-picker', config)(<TimePicker />)}
  69. </Form.Item>
  70. <Form.Item
  71. wrapperCol={{
  72. xs: { span: 24, offset: 0 },
  73. sm: { span: 16, offset: 8 },
  74. }}
  75. >
  76. <Button type="primary" htmlType="submit">
  77. Submit
  78. </Button>
  79. </Form.Item>
  80. </Form>
  81. );
  82. }
  83. }
  84. const WrappedTimeRelatedForm = Form.create({ name: 'time_related_controls' })(TimeRelatedForm);
  85. ReactDOM.render(<WrappedTimeRelatedForm />, mountNode);

Form 表单 - 图8

自定义表单控件

自定义或第三方的表单控件,也可以与 Form 组件一起使用。只要该组件遵循以下的约定:

  • 提供受控属性 value 或其它与 valuePropName 的值同名的属性。

  • 提供 onChange 事件或 trigger 的值同名的事件。

  • 支持 ref:

    • React@16.3.0 之前只有 Class 组件支持。

    • React@16.3.0 及之后可以通过 forwardRef 添加 ref 支持。(示例

  1. import { Form, Input, Select, Button } from 'antd';
  2. const { Option } = Select;
  3. class PriceInput extends React.Component {
  4. static getDerivedStateFromProps(nextProps) {
  5. // Should be a controlled component.
  6. if ('value' in nextProps) {
  7. return {
  8. ...(nextProps.value || {}),
  9. };
  10. }
  11. return null;
  12. }
  13. constructor(props) {
  14. super(props);
  15. const value = props.value || {};
  16. this.state = {
  17. number: value.number || 0,
  18. currency: value.currency || 'rmb',
  19. };
  20. }
  21. handleNumberChange = e => {
  22. const number = parseInt(e.target.value || 0, 10);
  23. if (Number.isNaN(number)) {
  24. return;
  25. }
  26. if (!('value' in this.props)) {
  27. this.setState({ number });
  28. }
  29. this.triggerChange({ number });
  30. };
  31. handleCurrencyChange = currency => {
  32. if (!('value' in this.props)) {
  33. this.setState({ currency });
  34. }
  35. this.triggerChange({ currency });
  36. };
  37. triggerChange = changedValue => {
  38. // Should provide an event to pass value to Form.
  39. const onChange = this.props.onChange;
  40. if (onChange) {
  41. onChange(Object.assign({}, this.state, changedValue));
  42. }
  43. };
  44. render() {
  45. const { size } = this.props;
  46. const state = this.state;
  47. return (
  48. <span>
  49. <Input
  50. type="text"
  51. size={size}
  52. value={state.number}
  53. onChange={this.handleNumberChange}
  54. style={{ width: '65%', marginRight: '3%' }}
  55. />
  56. <Select
  57. value={state.currency}
  58. size={size}
  59. style={{ width: '32%' }}
  60. onChange={this.handleCurrencyChange}
  61. >
  62. <Option value="rmb">RMB</Option>
  63. <Option value="dollar">Dollar</Option>
  64. </Select>
  65. </span>
  66. );
  67. }
  68. }
  69. class Demo extends React.Component {
  70. handleSubmit = e => {
  71. e.preventDefault();
  72. this.props.form.validateFields((err, values) => {
  73. if (!err) {
  74. console.log('Received values of form: ', values);
  75. }
  76. });
  77. };
  78. checkPrice = (rule, value, callback) => {
  79. if (value.number > 0) {
  80. callback();
  81. return;
  82. }
  83. callback('Price must greater than zero!');
  84. };
  85. render() {
  86. const { getFieldDecorator } = this.props.form;
  87. return (
  88. <Form layout="inline" onSubmit={this.handleSubmit}>
  89. <Form.Item label="Price">
  90. {getFieldDecorator('price', {
  91. initialValue: { number: 0, currency: 'rmb' },
  92. rules: [{ validator: this.checkPrice }],
  93. })(<PriceInput />)}
  94. </Form.Item>
  95. <Form.Item>
  96. <Button type="primary" htmlType="submit">
  97. Submit
  98. </Button>
  99. </Form.Item>
  100. </Form>
  101. );
  102. }
  103. }
  104. const WrappedDemo = Form.create({ name: 'customized_form_controls' })(Demo);
  105. ReactDOM.render(<WrappedDemo />, mountNode);

Form 表单 - 图9

表单数据存储于上层组件

通过使用 onFieldsChangemapPropsToFields,可以把表单的数据存储到上层组件或者 Reduxdva 中,更多可参考 rc-form 示例

注意:mapPropsToFields 里面返回的表单域数据必须使用 Form.createFormField 包装。

  1. import { Form, Input } from 'antd';
  2. const CustomizedForm = Form.create({
  3. name: 'global_state',
  4. onFieldsChange(props, changedFields) {
  5. props.onChange(changedFields);
  6. },
  7. mapPropsToFields(props) {
  8. return {
  9. username: Form.createFormField({
  10. ...props.username,
  11. value: props.username.value,
  12. }),
  13. };
  14. },
  15. onValuesChange(_, values) {
  16. console.log(values);
  17. },
  18. })(props => {
  19. const { getFieldDecorator } = props.form;
  20. return (
  21. <Form layout="inline">
  22. <Form.Item label="Username">
  23. {getFieldDecorator('username', {
  24. rules: [{ required: true, message: 'Username is required!' }],
  25. })(<Input />)}
  26. </Form.Item>
  27. </Form>
  28. );
  29. });
  30. class Demo extends React.Component {
  31. state = {
  32. fields: {
  33. username: {
  34. value: 'benjycui',
  35. },
  36. },
  37. };
  38. handleFormChange = changedFields => {
  39. this.setState(({ fields }) => ({
  40. fields: { ...fields, ...changedFields },
  41. }));
  42. };
  43. render() {
  44. const fields = this.state.fields;
  45. return (
  46. <div>
  47. <CustomizedForm {...fields} onChange={this.handleFormChange} />
  48. <pre className="language-bash">{JSON.stringify(fields, null, 2)}</pre>
  49. </div>
  50. );
  51. }
  52. }
  53. ReactDOM.render(<Demo />, mountNode);

Form 表单 - 图10

自行处理表单数据

使用 Form.create 处理后的表单具有自动收集数据并校验的功能,但如果您不需要这个功能,或者默认的行为无法满足业务需求,可以选择不使用 Form.create 并自行处理数据。

  1. import { Form, InputNumber } from 'antd';
  2. function validatePrimeNumber(number) {
  3. if (number === 11) {
  4. return {
  5. validateStatus: 'success',
  6. errorMsg: null,
  7. };
  8. }
  9. return {
  10. validateStatus: 'error',
  11. errorMsg: 'The prime between 8 and 12 is 11!',
  12. };
  13. }
  14. class RawForm extends React.Component {
  15. state = {
  16. number: {
  17. value: 11,
  18. },
  19. };
  20. handleNumberChange = value => {
  21. this.setState({
  22. number: {
  23. ...validatePrimeNumber(value),
  24. value,
  25. },
  26. });
  27. };
  28. render() {
  29. const formItemLayout = {
  30. labelCol: { span: 7 },
  31. wrapperCol: { span: 12 },
  32. };
  33. const number = this.state.number;
  34. const tips =
  35. 'A prime is a natural number greater than 1 that has no positive divisors other than 1 and itself.';
  36. return (
  37. <Form>
  38. <Form.Item
  39. {...formItemLayout}
  40. label="Prime between 8 & 12"
  41. validateStatus={number.validateStatus}
  42. help={number.errorMsg || tips}
  43. >
  44. <InputNumber min={8} max={12} value={number.value} onChange={this.handleNumberChange} />
  45. </Form.Item>
  46. </Form>
  47. );
  48. }
  49. }
  50. ReactDOM.render(<RawForm />, mountNode);

Form 表单 - 图11

自定义校验

我们提供了 validateStatus help hasFeedback 等属性,你可以不需要使用 Form.creategetFieldDecorator,自己定义校验的时机和内容。

  • validateStatus: 校验状态,可选 'success', 'warning', 'error', 'validating'。

  • hasFeedback:用于给输入框添加反馈图标。

  • help:设置校验文案。

  1. import { Form, Input, DatePicker, TimePicker, Select, Cascader, InputNumber } from 'antd';
  2. const { Option } = Select;
  3. const formItemLayout = {
  4. labelCol: {
  5. xs: { span: 24 },
  6. sm: { span: 5 },
  7. },
  8. wrapperCol: {
  9. xs: { span: 24 },
  10. sm: { span: 12 },
  11. },
  12. };
  13. ReactDOM.render(
  14. <Form {...formItemLayout}>
  15. <Form.Item
  16. label="Fail"
  17. validateStatus="error"
  18. help="Should be combination of numbers & alphabets"
  19. >
  20. <Input placeholder="unavailable choice" id="error" />
  21. </Form.Item>
  22. <Form.Item label="Warning" validateStatus="warning">
  23. <Input placeholder="Warning" id="warning" />
  24. </Form.Item>
  25. <Form.Item
  26. label="Validating"
  27. hasFeedback
  28. validateStatus="validating"
  29. help="The information is being validated..."
  30. >
  31. <Input placeholder="I'm the content is being validated" id="validating" />
  32. </Form.Item>
  33. <Form.Item label="Success" hasFeedback validateStatus="success">
  34. <Input placeholder="I'm the content" id="success" />
  35. </Form.Item>
  36. <Form.Item label="Warning" hasFeedback validateStatus="warning">
  37. <Input placeholder="Warning" id="warning2" />
  38. </Form.Item>
  39. <Form.Item
  40. label="Fail"
  41. hasFeedback
  42. validateStatus="error"
  43. help="Should be combination of numbers & alphabets"
  44. >
  45. <Input placeholder="unavailable choice" id="error2" />
  46. </Form.Item>
  47. <Form.Item label="Success" hasFeedback validateStatus="success">
  48. <DatePicker style={{ width: '100%' }} />
  49. </Form.Item>
  50. <Form.Item label="Warning" hasFeedback validateStatus="warning">
  51. <TimePicker style={{ width: '100%' }} />
  52. </Form.Item>
  53. <Form.Item label="Error" hasFeedback validateStatus="error">
  54. <Select defaultValue="1">
  55. <Option value="1">Option 1</Option>
  56. <Option value="2">Option 2</Option>
  57. <Option value="3">Option 3</Option>
  58. </Select>
  59. </Form.Item>
  60. <Form.Item
  61. label="Validating"
  62. hasFeedback
  63. validateStatus="validating"
  64. help="The information is being validated..."
  65. >
  66. <Cascader defaultValue={['1']} options={[]} />
  67. </Form.Item>
  68. <Form.Item label="inline" style={{ marginBottom: 0 }}>
  69. <Form.Item
  70. validateStatus="error"
  71. help="Please select the correct date"
  72. style={{ display: 'inline-block', width: 'calc(50% - 12px)' }}
  73. >
  74. <DatePicker />
  75. </Form.Item>
  76. <span style={{ display: 'inline-block', width: '24px', textAlign: 'center' }}>-</span>
  77. <Form.Item style={{ display: 'inline-block', width: 'calc(50% - 12px)' }}>
  78. <DatePicker />
  79. </Form.Item>
  80. </Form.Item>
  81. <Form.Item label="Success" hasFeedback validateStatus="success">
  82. <InputNumber style={{ width: '100%' }} />
  83. </Form.Item>
  84. </Form>,
  85. mountNode,
  86. );

Form 表单 - 图12

表单联动

使用 setFieldsValue 来动态设置其他控件的值。

  1. import { Form, Select, Input, Button } from 'antd';
  2. const { Option } = Select;
  3. class App extends React.Component {
  4. handleSubmit = e => {
  5. e.preventDefault();
  6. this.props.form.validateFields((err, values) => {
  7. if (!err) {
  8. console.log('Received values of form: ', values);
  9. }
  10. });
  11. };
  12. handleSelectChange = value => {
  13. console.log(value);
  14. this.props.form.setFieldsValue({
  15. note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
  16. });
  17. };
  18. render() {
  19. const { getFieldDecorator } = this.props.form;
  20. return (
  21. <Form labelCol={{ span: 5 }} wrapperCol={{ span: 12 }} onSubmit={this.handleSubmit}>
  22. <Form.Item label="Note">
  23. {getFieldDecorator('note', {
  24. rules: [{ required: true, message: 'Please input your note!' }],
  25. })(<Input />)}
  26. </Form.Item>
  27. <Form.Item label="Gender">
  28. {getFieldDecorator('gender', {
  29. rules: [{ required: true, message: 'Please select your gender!' }],
  30. })(
  31. <Select
  32. placeholder="Select a option and change input text above"
  33. onChange={this.handleSelectChange}
  34. >
  35. <Option value="male">male</Option>
  36. <Option value="female">female</Option>
  37. </Select>,
  38. )}
  39. </Form.Item>
  40. <Form.Item wrapperCol={{ span: 12, offset: 5 }}>
  41. <Button type="primary" htmlType="submit">
  42. Submit
  43. </Button>
  44. </Form.Item>
  45. </Form>
  46. );
  47. }
  48. }
  49. const WrappedApp = Form.create({ name: 'coordinated' })(App);
  50. ReactDOM.render(<WrappedApp />, mountNode);

Form 表单 - 图13

表单布局

表单有三种布局。

  1. import { Form, Input, Button, Radio } from 'antd';
  2. class FormLayoutDemo extends React.Component {
  3. constructor() {
  4. super();
  5. this.state = {
  6. formLayout: 'horizontal',
  7. };
  8. }
  9. handleFormLayoutChange = e => {
  10. this.setState({ formLayout: e.target.value });
  11. };
  12. render() {
  13. const { formLayout } = this.state;
  14. const formItemLayout =
  15. formLayout === 'horizontal'
  16. ? {
  17. labelCol: { span: 4 },
  18. wrapperCol: { span: 14 },
  19. }
  20. : null;
  21. const buttonItemLayout =
  22. formLayout === 'horizontal'
  23. ? {
  24. wrapperCol: { span: 14, offset: 4 },
  25. }
  26. : null;
  27. return (
  28. <div>
  29. <Form layout={formLayout}>
  30. <Form.Item label="Form Layout" {...formItemLayout}>
  31. <Radio.Group defaultValue="horizontal" onChange={this.handleFormLayoutChange}>
  32. <Radio.Button value="horizontal">Horizontal</Radio.Button>
  33. <Radio.Button value="vertical">Vertical</Radio.Button>
  34. <Radio.Button value="inline">Inline</Radio.Button>
  35. </Radio.Group>
  36. </Form.Item>
  37. <Form.Item label="Field A" {...formItemLayout}>
  38. <Input placeholder="input placeholder" />
  39. </Form.Item>
  40. <Form.Item label="Field B" {...formItemLayout}>
  41. <Input placeholder="input placeholder" />
  42. </Form.Item>
  43. <Form.Item {...buttonItemLayout}>
  44. <Button type="primary">Submit</Button>
  45. </Form.Item>
  46. </Form>
  47. </div>
  48. );
  49. }
  50. }
  51. ReactDOM.render(<FormLayoutDemo />, mountNode);

Form 表单 - 图14

动态校验规则

根据不同情况执行不同的校验规则。

  1. import { Form, Input, Button, Checkbox } from 'antd';
  2. const formItemLayout = {
  3. labelCol: { span: 4 },
  4. wrapperCol: { span: 8 },
  5. };
  6. const formTailLayout = {
  7. labelCol: { span: 4 },
  8. wrapperCol: { span: 8, offset: 4 },
  9. };
  10. class DynamicRule extends React.Component {
  11. state = {
  12. checkNick: false,
  13. };
  14. check = () => {
  15. this.props.form.validateFields(err => {
  16. if (!err) {
  17. console.info('success');
  18. }
  19. });
  20. };
  21. handleChange = e => {
  22. this.setState(
  23. {
  24. checkNick: e.target.checked,
  25. },
  26. () => {
  27. this.props.form.validateFields(['nickname'], { force: true });
  28. },
  29. );
  30. };
  31. render() {
  32. const { getFieldDecorator } = this.props.form;
  33. return (
  34. <div>
  35. <Form.Item {...formItemLayout} label="Name">
  36. {getFieldDecorator('username', {
  37. rules: [
  38. {
  39. required: true,
  40. message: 'Please input your name',
  41. },
  42. ],
  43. })(<Input placeholder="Please input your name" />)}
  44. </Form.Item>
  45. <Form.Item {...formItemLayout} label="Nickname">
  46. {getFieldDecorator('nickname', {
  47. rules: [
  48. {
  49. required: this.state.checkNick,
  50. message: 'Please input your nickname',
  51. },
  52. ],
  53. })(<Input placeholder="Please input your nickname" />)}
  54. </Form.Item>
  55. <Form.Item {...formTailLayout}>
  56. <Checkbox checked={this.state.checkNick} onChange={this.handleChange}>
  57. Nickname is required
  58. </Checkbox>
  59. </Form.Item>
  60. <Form.Item {...formTailLayout}>
  61. <Button type="primary" onClick={this.check}>
  62. Check
  63. </Button>
  64. </Form.Item>
  65. </div>
  66. );
  67. }
  68. }
  69. const WrappedDynamicRule = Form.create({ name: 'dynamic_rule' })(DynamicRule);
  70. ReactDOM.render(<WrappedDynamicRule />, mountNode);

Form 表单 - 图15

校验其他组件

以上演示没有出现的表单控件对应的校验演示。

  1. import {
  2. Form,
  3. Select,
  4. InputNumber,
  5. Switch,
  6. Radio,
  7. Slider,
  8. Button,
  9. Upload,
  10. Icon,
  11. Rate,
  12. Checkbox,
  13. Row,
  14. Col,
  15. } from 'antd';
  16. const { Option } = Select;
  17. class Demo extends React.Component {
  18. handleSubmit = e => {
  19. e.preventDefault();
  20. this.props.form.validateFields((err, values) => {
  21. if (!err) {
  22. console.log('Received values of form: ', values);
  23. }
  24. });
  25. };
  26. normFile = e => {
  27. console.log('Upload event:', e);
  28. if (Array.isArray(e)) {
  29. return e;
  30. }
  31. return e && e.fileList;
  32. };
  33. render() {
  34. const { getFieldDecorator } = this.props.form;
  35. const formItemLayout = {
  36. labelCol: { span: 6 },
  37. wrapperCol: { span: 14 },
  38. };
  39. return (
  40. <Form {...formItemLayout} onSubmit={this.handleSubmit}>
  41. <Form.Item label="Plain Text">
  42. <span className="ant-form-text">China</span>
  43. </Form.Item>
  44. <Form.Item label="Select" hasFeedback>
  45. {getFieldDecorator('select', {
  46. rules: [{ required: true, message: 'Please select your country!' }],
  47. })(
  48. <Select placeholder="Please select a country">
  49. <Option value="china">China</Option>
  50. <Option value="usa">U.S.A</Option>
  51. </Select>,
  52. )}
  53. </Form.Item>
  54. <Form.Item label="Select[multiple]">
  55. {getFieldDecorator('select-multiple', {
  56. rules: [
  57. { required: true, message: 'Please select your favourite colors!', type: 'array' },
  58. ],
  59. })(
  60. <Select mode="multiple" placeholder="Please select favourite colors">
  61. <Option value="red">Red</Option>
  62. <Option value="green">Green</Option>
  63. <Option value="blue">Blue</Option>
  64. </Select>,
  65. )}
  66. </Form.Item>
  67. <Form.Item label="InputNumber">
  68. {getFieldDecorator('input-number', { initialValue: 3 })(<InputNumber min={1} max={10} />)}
  69. <span className="ant-form-text"> machines</span>
  70. </Form.Item>
  71. <Form.Item label="Switch">
  72. {getFieldDecorator('switch', { valuePropName: 'checked' })(<Switch />)}
  73. </Form.Item>
  74. <Form.Item label="Slider">
  75. {getFieldDecorator('slider')(
  76. <Slider
  77. marks={{
  78. 0: 'A',
  79. 20: 'B',
  80. 40: 'C',
  81. 60: 'D',
  82. 80: 'E',
  83. 100: 'F',
  84. }}
  85. />,
  86. )}
  87. </Form.Item>
  88. <Form.Item label="Radio.Group">
  89. {getFieldDecorator('radio-group')(
  90. <Radio.Group>
  91. <Radio value="a">item 1</Radio>
  92. <Radio value="b">item 2</Radio>
  93. <Radio value="c">item 3</Radio>
  94. </Radio.Group>,
  95. )}
  96. </Form.Item>
  97. <Form.Item label="Radio.Button">
  98. {getFieldDecorator('radio-button')(
  99. <Radio.Group>
  100. <Radio.Button value="a">item 1</Radio.Button>
  101. <Radio.Button value="b">item 2</Radio.Button>
  102. <Radio.Button value="c">item 3</Radio.Button>
  103. </Radio.Group>,
  104. )}
  105. </Form.Item>
  106. <Form.Item label="Checkbox.Group">
  107. {getFieldDecorator('checkbox-group', {
  108. initialValue: ['A', 'B'],
  109. })(
  110. <Checkbox.Group style={{ width: '100%' }}>
  111. <Row>
  112. <Col span={8}>
  113. <Checkbox value="A">A</Checkbox>
  114. </Col>
  115. <Col span={8}>
  116. <Checkbox disabled value="B">
  117. B
  118. </Checkbox>
  119. </Col>
  120. <Col span={8}>
  121. <Checkbox value="C">C</Checkbox>
  122. </Col>
  123. <Col span={8}>
  124. <Checkbox value="D">D</Checkbox>
  125. </Col>
  126. <Col span={8}>
  127. <Checkbox value="E">E</Checkbox>
  128. </Col>
  129. </Row>
  130. </Checkbox.Group>,
  131. )}
  132. </Form.Item>
  133. <Form.Item label="Rate">
  134. {getFieldDecorator('rate', {
  135. initialValue: 3.5,
  136. })(<Rate />)}
  137. </Form.Item>
  138. <Form.Item label="Upload" extra="longgggggggggggggggggggggggggggggggggg">
  139. {getFieldDecorator('upload', {
  140. valuePropName: 'fileList',
  141. getValueFromEvent: this.normFile,
  142. })(
  143. <Upload name="logo" action="/upload.do" listType="picture">
  144. <Button>
  145. <Icon type="upload" /> Click to upload
  146. </Button>
  147. </Upload>,
  148. )}
  149. </Form.Item>
  150. <Form.Item label="Dragger">
  151. <div className="dropbox">
  152. {getFieldDecorator('dragger', {
  153. valuePropName: 'fileList',
  154. getValueFromEvent: this.normFile,
  155. })(
  156. <Upload.Dragger name="files" action="/upload.do">
  157. <p className="ant-upload-drag-icon">
  158. <Icon type="inbox" />
  159. </p>
  160. <p className="ant-upload-text">Click or drag file to this area to upload</p>
  161. <p className="ant-upload-hint">Support for a single or bulk upload.</p>
  162. </Upload.Dragger>,
  163. )}
  164. </div>
  165. </Form.Item>
  166. <Form.Item wrapperCol={{ span: 12, offset: 6 }}>
  167. <Button type="primary" htmlType="submit">
  168. Submit
  169. </Button>
  170. </Form.Item>
  171. </Form>
  172. );
  173. }
  174. }
  175. const WrappedDemo = Form.create({ name: 'validate_other' })(Demo);
  176. ReactDOM.render(<WrappedDemo />, mountNode);
  1. #components-form-demo-validate-other .dropbox {
  2. height: 180px;
  3. line-height: 1.5;
  4. }

API

Form

更多示例参考 rc-form

参数说明类型默认值
formForm.create() 包装过的组件会自带 this.props.form 属性object-
hideRequiredMark隐藏所有表单项的必选标记Booleanfalse
labelAlignlabel 标签的文本对齐方式'left' | 'right''right'
labelCol(3.14.0 新增,之前的版本只能设置到 FormItem 上。)label 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12}sm: {span: 3, offset: 12}object
layout表单布局'horizontal'|'vertical'|'inline''horizontal'
onSubmit数据验证成功后回调事件Function(e:Event)
wrapperCol(3.14.0 新增,之前的版本只能设置到 FormItem 上。)需要为输入控件设置布局样式时,使用该属性,用法同 labelColobject
colon配置 Form.Item 的 colon 的默认值booleantrue

Form.create(options)

使用方式如下:

  1. class CustomizedForm extends React.Component {}
  2. CustomizedForm = Form.create({})(CustomizedForm);

options 的配置项如下。

参数说明类型
mapPropsToFields把父组件的属性映射到表单项上(如:把 Redux store 中的值读出),需要对返回值中的表单域数据用 Form.createFormField 标记,注意表单项将变成受控组件, error 等也需要一并手动传入(props) => ({ [fieldName]: FormField { value } })
name设置表单域内字段 id 的前缀-
validateMessages默认校验信息,可用于把默认错误信息改为中文等,格式与 newMessages 返回值一致Object { [nested.path]: String }
onFieldsChangeForm.Item 子节点的值(包括 error)发生改变时触发,可以把对应的值转存到 Redux storeFunction(props, changedFields, allFields)
onValuesChange任一表单域的值发生改变时的回调(props, changedValues, allValues) => void

经过 Form.create 之后如果要拿到 ref,可以使用 rc-form 提供的 wrappedComponentRef详细内容可以查看这里

  1. class CustomizedForm extends React.Component { ... }
  2. // use wrappedComponentRef
  3. const EnhancedForm = Form.create()(CustomizedForm);
  4. <EnhancedForm wrappedComponentRef={(form) => this.form = form} />
  5. this.form // => The instance of CustomizedForm

经过 Form.create 包装的组件将会自带 this.props.form 属性,this.props.form 提供的 API 如下:

注意:使用 getFieldsValue getFieldValue setFieldsValue 等时,应确保对应的 field 已经用 getFieldDecorator 注册过了。

方法 说明 类型
getFieldDecorator用于和表单进行双向绑定,详见下方描述
getFieldError获取某个输入控件的 ErrorFunction(name)
getFieldsError获取一组输入控件的 Error ,如不传入参数,则获取全部组件的 ErrorFunction([names: string[]])
getFieldsValue获取一组输入控件的值,如不传入参数,则获取全部组件的值Function([fieldNames: string[]])
getFieldValue获取一个输入控件的值Function(fieldName: string)
isFieldsTouched判断是否任一输入控件经历过 getFieldDecorator 的值收集时机 options.trigger(names?: string[]) => boolean
isFieldTouched判断一个输入控件是否经历过 getFieldDecorator 的值收集时机 options.trigger(name: string) => boolean
isFieldValidating判断一个输入控件是否在校验状态Function(name)
resetFields重置一组输入控件的值(为 initialValue)与状态,如不传入参数,则重置所有组件Function([names: string[]])
setFields设置一组输入控件的值与错误状态:代码({ [fieldName]: {value: any, errors: [Error] }}) => void
setFieldsValue设置一组输入控件的值(注意:不要在 componentWillReceiveProps 内使用,否则会导致死循环,原因({ [fieldName]: value }) => void
validateFields校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件( [fieldNames: string[]], [options: object], callback(errors, values)) => void
validateFieldsAndScrollvalidateFields 相似,但校验完后,如果校验不通过的菜单域不在可见范围内,则自动滚动进可见范围参考 validateFields

validateFields/validateFieldsAndScroll

  1. const {
  2. form: { validateFields },
  3. } = this.props;
  4. validateFields((errors, values) => {
  5. // ...
  6. });
  7. validateFields(['field1', 'field2'], (errors, values) => {
  8. // ...
  9. });
  10. validateFields(['field1', 'field2'], options, (errors, values) => {
  11. // ...
  12. });
参数说明类型默认值
options.first若为 true,则每一表单域的都会在碰到第一个失败了的校验规则后停止校验booleanfalse
options.firstFields指定表单域会在碰到第一个失败了的校验规则后停止校验String[][]
options.force对已经校验过的表单域,在 validateTrigger 再次被触发时是否再次校验booleanfalse
options.scroll定义 validateFieldsAndScroll 的滚动行为,详细配置见 dom-scroll-into-view configObject{}

validateFields 的 callback 参数示例

  • errors:
  1. {
  2. "username": {
  3. "errors": [
  4. {
  5. "message": "Please input your username!",
  6. "field": "username"
  7. }
  8. ]
  9. },
  10. "password": {
  11. "errors": [
  12. {
  13. "message": "Please input your Password!",
  14. "field": "password"
  15. }
  16. ]
  17. }
  18. }
  • values:
  1. {
  2. "username": "username",
  3. "password": "password",
  4. }

Form.createFormField

用于标记 mapPropsToFields 返回的表单域数据,例子

this.props.form.getFieldDecorator(id, options)

经过 getFieldDecorator 包装的控件,表单控件会自动添加 value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:

  • 不再需要也不应该onChange 来做同步,但还是可以继续监听 onChange 等事件。

  • 你不能用控件的 value defaultValue 等属性来设置表单域的值,默认值可以用 getFieldDecorator 里的 initialValue

  • 你不应该用 setState,可以使用 this.props.form.setFieldsValue 来动态改变表单值。

特别注意

如果使用的是 react@<15.3.0,则 getFieldDecorator 调用不能位于纯函数组件中: https://github.com/facebook/react/pull/6534

getFieldDecorator(id, options) 参数

参数说明类型默认值
id必填输入控件唯一标志。支持嵌套式的写法string
options.getValueFromEvent可以把 onChange 的参数(如 event)转化为控件的值function(..args)reference
options.initialValue子节点的初始值,类型、可选值均由子节点决定(注意:由于内部校验时使用 === 判断是否变化,建议使用变量缓存所需设置的值而非直接使用字面量))
options.normalize转换默认的 value 给控件,一个选择全部的例子function(value, prevValue, allValues): any-
options.preserve即便字段不再使用,也保留该字段的值boolean-
options.rules校验规则,参考下方文档object[]
options.trigger收集子节点的值的时机string'onChange'
options.validateFirst当某一规则校验不通过时,是否停止剩下的规则的校验booleanfalse
options.validateTrigger校验子节点值的时机string|string[]'onChange'
options.valuePropName子节点的值的属性,如 Switch 的是 'checked'string'value'

更多参数请查看 rc-form option

Form.Item

注意:一个 Form.Item 建议只放一个被 getFieldDecorator 装饰过的 child,当有多个被装饰过的 child 时,help required validateStatus 无法自动生成。

参数说明类型默认值版本
colon配合 label 属性使用,表示是否显示 label 后面的冒号booleantrue
extra额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。string|ReactNode
hasFeedback配合 validateStatus 属性使用,展示校验状态图标,建议只配合 Input 组件使用booleanfalse
help提示信息,如不设置,则会根据校验规则自动生成string|ReactNode
htmlFor设置子元素 label htmlFor 属性string3.17.0
labellabel 标签的文本string|ReactNode
labelCollabel 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12}sm: {span: 3, offset: 12}。在 3.14.0 之后,你可以通过 Form 的 labelCol 进行统一设置。当和 Form 同时设置时,以 FormItem 为准。object
required是否必填,如不设置,则会根据校验规则自动生成booleanfalse
validateStatus校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating'string
wrapperCol需要为输入控件设置布局样式时,使用该属性,用法同 labelCol。在 3.14.0 之后,你可以通过 Form 的 labelCol 进行统一设置。当和 Form 同时设置时,以 FormItem 为准。object

校验规则

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

更多高级用法可研究 async-validator

在 TypeScript 中使用

  1. import { Form } from 'antd';
  2. import { FormComponentProps } from 'antd/lib/form';
  3. interface UserFormProps extends FormComponentProps {
  4. age: number;
  5. name: string;
  6. }
  7. class UserForm extends React.Component<UserFormProps, any> {
  8. // ...
  9. }
  10. const App =
  11. Form.create <
  12. UserFormProps >
  13. {
  14. // ...
  15. }(UserForm);