• Form表单
    • 何时使用
    • 表单
    • 表单域
    • 代码演示
    • API
      • Form
      • Form.create(options)
      • validateFields/validateFieldsAndScroll
        • validateFields 的 callback 参数示例
      • Form.createFormField
      • this.props.form.getFieldDecorator(id, options)
        • 特别注意
        • getFieldDecorator(id, options) 参数
      • Form.Item
      • 校验规则
    • 在 TypeScript 中使用

    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,可以把表单的数据存储到上层组件或者 Redux、dva 中,更多可参考 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);