Form表单

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

表单

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

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

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

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

表单域

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

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

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

代码演示

Form 表单 - 图1

水平登录栏

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

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

Form 表单 - 图2

登录框

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

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

Form 表单 - 图4

高级搜索

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

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

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

.c7n-advanced-search-form .c7n-form-item {
  display: flex;
}

.c7n-advanced-search-form .c7n-form-item-control-wrapper {
  flex: 1;
}

Form 表单 - 图5

弹出层中的新建表单

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

import { Button, Modal, Form, Input, Radio } from 'choerodon-ui';

const FormItem = Form.Item;

const CollectionCreateForm = Form.create()(
  // eslint-disable-next-line
  class extends React.Component {
    render() {
      const { visible, onCancel, onCreate, form } = this.props;
      const { getFieldDecorator } = form;
      return (
        <Modal
          visible={visible}
          title="Create a new collection"
          okText="Create"
          onCancel={onCancel}
          onOk={onCreate}
        >
          <Form layout="vertical">
            <FormItem label="Title">
              {getFieldDecorator('title', {
                rules: [{ required: true, message: 'Please input the title of collection!' }],
              })(<Input />)}
            </FormItem>
            <FormItem label="Description">
              {getFieldDecorator('description')(<Input type="textarea" />)}
            </FormItem>
            <FormItem className="collection-create-form_last-form-item">
              {getFieldDecorator('modifier', {
                initialValue: 'public',
              })(
                <Radio.Group>
                  <Radio value="public">Public</Radio>
                  <Radio value="private">Private</Radio>
                </Radio.Group>,
              )}
            </FormItem>
          </Form>
        </Modal>
      );
    }
  },
);

class CollectionsPage extends React.Component {
  state = {
    visible: false,
  };

  showModal = () => {
    this.setState({ visible: true });
  };

  handleCancel = () => {
    this.setState({ visible: false });
  };

  handleCreate = () => {
    const form = this.formRef.props.form;
    form.validateFields((err, values) => {
      if (err) {
        return;
      }

      console.log('Received values of form: ', values);
      form.resetFields();
      this.setState({ visible: false });
    });
  };

  saveFormRef = formRef => {
    this.formRef = formRef;
  };

  render() {
    return (
      <div>
        <Button type="primary" onClick={this.showModal}>
          New Collection
        </Button>
        <CollectionCreateForm
          wrappedComponentRef={this.saveFormRef}
          visible={this.state.visible}
          onCancel={this.handleCancel}
          onCreate={this.handleCreate}
        />
      </div>
    );
  }
}

ReactDOM.render(<CollectionsPage />, mountNode);
.collection-create-form_last-form-item {
  margin-bottom: 0;
}

Form 表单 - 图6

动态增减表单项

动态增加、减少表单项。

import { Form, Input, Icon, Button } from 'choerodon-ui';

const FormItem = Form.Item;

let uuid = 0;
class DynamicFieldSet extends React.Component {
  remove = k => {
    const { form } = this.props;
    // can use data-binding to get
    const keys = form.getFieldValue('keys');
    // We need at least one passenger
    if (keys.length === 1) {
      return;
    }

    // can use data-binding to set
    form.setFieldsValue({
      keys: keys.filter(key => key !== k),
    });
  };

  add = () => {
    const { form } = this.props;
    // can use data-binding to get
    const keys = form.getFieldValue('keys');
    const nextKeys = keys.concat(uuid);
    uuid++;
    // can use data-binding to set
    // important! notify form to detect changes
    form.setFieldsValue({
      keys: nextKeys,
    });
  };

  handleSubmit = e => {
    e.preventDefault();
    this.props.form.validateFields((err, values) => {
      if (!err) {
        console.log('Received values of form: ', values);
      }
    });
  };

  render() {
    const { getFieldDecorator, getFieldValue } = this.props.form;
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 4 },
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 20 },
      },
    };
    const formItemLayoutWithOutLabel = {
      wrapperCol: {
        xs: { span: 24, offset: 0 },
        sm: { span: 20, offset: 4 },
      },
    };
    getFieldDecorator('keys', { initialValue: [] });
    const keys = getFieldValue('keys');
    const formItems = keys.map((k, index) => {
      return (
        <FormItem
          {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
          label={index === 0 ? 'Passengers' : ''}
          required={false}
          key={k}
        >
          {getFieldDecorator(`names[${k}]`, {
            validateTrigger: ['onChange', 'onBlur'],
            rules: [
              {
                required: true,
                whitespace: true,
                message: "Please input passenger's name or delete this field.",
              },
            ],
          })(<Input placeholder="passenger name" style={{ width: '60%', marginRight: 8 }} />)}
          {keys.length > 1 ? (
            <Icon
              className="dynamic-delete-button"
              type="minus-circle-o"
              disabled={keys.length === 1}
              onClick={() => this.remove(k)}
            />
          ) : null}
        </FormItem>
      );
    });
    return (
      <Form onSubmit={this.handleSubmit}>
        {formItems}
        <FormItem {...formItemLayoutWithOutLabel}>
          <Button type="dashed" onClick={this.add} style={{ width: '60%' }}>
            <Icon type="plus" /> Add field
          </Button>
        </FormItem>
        <FormItem {...formItemLayoutWithOutLabel}>
          <Button type="primary" htmlType="submit">
            Submit
          </Button>
        </FormItem>
      </Form>
    );
  }
}

const WrappedDynamicFieldSet = Form.create()(DynamicFieldSet);
ReactDOM.render(<WrappedDynamicFieldSet />, mountNode);
.dynamic-delete-button {
  cursor: pointer;
  position: relative;
  top: 4px;
  font-size: 24px;
  color: #999;
  transition: all 0.3s;
}
.dynamic-delete-button:hover {
  color: #777;
}
.dynamic-delete-button[disabled] {
  cursor: not-allowed;
  opacity: 0.5;
}

Form 表单 - 图7

时间类控件

import { Form, DatePicker, TimePicker, Button } from 'choerodon-ui';

const FormItem = Form.Item;
const MonthPicker = DatePicker.MonthPicker;
const RangePicker = DatePicker.RangePicker;

class TimeRelatedForm extends React.Component {
  handleSubmit = e => {
    e.preventDefault();

    this.props.form.validateFields((err, fieldsValue) => {
      if (err) {
        return;
      }

      // Should format date value before submit.
      const rangeValue = fieldsValue['range-picker'];
      const rangeTimeValue = fieldsValue['range-time-picker'];
      const values = {
        ...fieldsValue,
        'date-picker': fieldsValue['date-picker'].format('YYYY-MM-DD'),
        'date-time-picker': fieldsValue['date-time-picker'].format('YYYY-MM-DD HH:mm:ss'),
        'month-picker': fieldsValue['month-picker'].format('YYYY-MM'),
        'range-picker': [rangeValue[0].format('YYYY-MM-DD'), rangeValue[1].format('YYYY-MM-DD')],
        'range-time-picker': [
          rangeTimeValue[0].format('YYYY-MM-DD HH:mm:ss'),
          rangeTimeValue[1].format('YYYY-MM-DD HH:mm:ss'),
        ],
        'time-picker': fieldsValue['time-picker'].format('HH:mm:ss'),
      };
      console.log('Received values of form: ', values);
    });
  };

  render() {
    const { getFieldDecorator } = this.props.form;
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 },
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 },
      },
    };
    const config = {
      rules: [{ type: 'object', required: true, message: 'Please select time!' }],
    };
    const rangeConfig = {
      rules: [{ type: 'array', required: true, message: 'Please select time!' }],
    };
    return (
      <Form onSubmit={this.handleSubmit}>
        <FormItem {...formItemLayout} label="DatePicker">
          {getFieldDecorator('date-picker', config)(<DatePicker />)}
        </FormItem>
        <FormItem {...formItemLayout} label="DatePicker[showTime]">
          {getFieldDecorator('date-time-picker', config)(
            <DatePicker showTime format="YYYY-MM-DD HH:mm:ss" />,
          )}
        </FormItem>
        <FormItem {...formItemLayout} label="MonthPicker">
          {getFieldDecorator('month-picker', config)(<MonthPicker />)}
        </FormItem>
        <FormItem {...formItemLayout} label="RangePicker">
          {getFieldDecorator('range-picker', rangeConfig)(<RangePicker />)}
        </FormItem>
        <FormItem {...formItemLayout} label="RangePicker[showTime]">
          {getFieldDecorator('range-time-picker', rangeConfig)(
            <RangePicker showTime format="YYYY-MM-DD HH:mm:ss" />,
          )}
        </FormItem>
        <FormItem {...formItemLayout} label="TimePicker">
          {getFieldDecorator('time-picker', config)(<TimePicker />)}
        </FormItem>
        <FormItem
          wrapperCol={{
            xs: { span: 24, offset: 0 },
            sm: { span: 16, offset: 8 },
          }}
        >
          <Button type="primary" htmlType="submit">
            Submit
          </Button>
        </FormItem>
      </Form>
    );
  }
}

const WrappedTimeRelatedForm = Form.create()(TimeRelatedForm);

ReactDOM.render(<WrappedTimeRelatedForm />, mountNode);

Form 表单 - 图8

自定义表单控件

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

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

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

  • 不能是函数式组件。

import { Form, Input, Select, Button } from 'choerodon-ui';

const FormItem = Form.Item;
const Option = Select.Option;

class PriceInput extends React.Component {
  constructor(props) {
    super(props);

    const value = this.props.value || {};
    this.state = {
      number: value.number || 0,
      currency: value.currency || 'rmb',
    };
  }

  componentWillReceiveProps(nextProps) {
    // Should be a controlled component.
    if ('value' in nextProps) {
      const value = nextProps.value;
      this.setState(value);
    }
  }

  handleNumberChange = e => {
    const number = parseInt(e.target.value || 0, 10);
    if (isNaN(number)) {
      return;
    }
    if (!('value' in this.props)) {
      this.setState({ number });
    }
    this.triggerChange({ number });
  };

  handleCurrencyChange = currency => {
    if (!('value' in this.props)) {
      this.setState({ currency });
    }
    this.triggerChange({ currency });
  };

  triggerChange = changedValue => {
    // Should provide an event to pass value to Form.
    const onChange = this.props.onChange;
    if (onChange) {
      onChange({ ...this.state, ...changedValue });
    }
  };

  render() {
    const { size } = this.props;
    const state = this.state;
    return (
      <span>
        <Input
          type="text"
          size={size}
          value={state.number}
          onChange={this.handleNumberChange}
          style={{ width: '65%', marginRight: '3%', lineHeight: '1.7' }}
        />
        <Select
          value={state.currency}
          size={size}
          style={{ width: '32%' }}
          onChange={this.handleCurrencyChange}
        >
          <Option value="rmb">RMB</Option>
          <Option value="dollar">Dollar</Option>
        </Select>
      </span>
    );
  }
}

class Demo extends React.Component {
  handleSubmit = e => {
    e.preventDefault();
    this.props.form.validateFields((err, values) => {
      if (!err) {
        console.log('Received values of form: ', values);
      }
    });
  };

  checkPrice = (rule, value, callback) => {
    if (value.number > 0) {
      callback();
      return;
    }
    callback('Price must greater than zero!');
  };

  render() {
    const { getFieldDecorator } = this.props.form;
    return (
      <Form layout="inline" onSubmit={this.handleSubmit}>
        <FormItem label="Price">
          {getFieldDecorator('price', {
            initialValue: { number: 0, currency: 'rmb' },
            rules: [{ validator: this.checkPrice }],
          })(<PriceInput />)}
        </FormItem>
        <FormItem>
          <Button type="primary" htmlType="submit">
            Submit
          </Button>
        </FormItem>
      </Form>
    );
  }
}

const WrappedDemo = Form.create()(Demo);

ReactDOM.render(<WrappedDemo />, mountNode);

Form 表单 - 图9

表单数据存储于上层组件

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

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

import { Form, Input } from 'choerodon-ui';

const FormItem = Form.Item;

const CustomizedForm = Form.create({
  onFieldsChange(props, changedFields) {
    props.onChange(changedFields);
  },
  mapPropsToFields(props) {
    return {
      username: Form.createFormField({
        ...props.username,
        value: props.username.value,
      }),
    };
  },
  onValuesChange(_, values) {
    console.log(values);
  },
})(props => {
  const { getFieldDecorator } = props.form;
  return (
    <Form layout="inline">
      <FormItem label="Username">
        {getFieldDecorator('username', {
          rules: [{ required: true, message: 'Username is required!' }],
        })(<Input />)}
      </FormItem>
    </Form>
  );
});

class Demo extends React.Component {
  state = {
    fields: {
      username: {
        value: 'benjycui',
      },
    },
  };

  handleFormChange = changedFields => {
    this.setState(({ fields }) => ({
      fields: { ...fields, ...changedFields },
    }));
  };

  render() {
    const fields = this.state.fields;
    return (
      <div>
        <CustomizedForm {...fields} onChange={this.handleFormChange} />
        <pre className="language-bash">{JSON.stringify(fields, null, 2)}</pre>
      </div>
    );
  }
}

ReactDOM.render(<Demo />, mountNode);

Form 表单 - 图10

自行处理表单数据

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

import { Form, InputNumber } from 'choerodon-ui';

const FormItem = Form.Item;

function validatePrimeNumber(number) {
  if (number === 11) {
    return {
      validateStatus: 'success',
      errorMsg: null,
    };
  }
  return {
    validateStatus: 'error',
    errorMsg: 'The prime between 8 and 12 is 11!',
  };
}

class RawForm extends React.Component {
  state = {
    number: {
      value: 11,
    },
  };

  handleNumberChange = value => {
    this.setState({
      number: {
        ...validatePrimeNumber(value),
        value,
      },
    });
  };

  render() {
    const formItemLayout = {
      labelCol: { span: 7 },
      wrapperCol: { span: 12 },
    };
    const number = this.state.number;
    const tips =
      'A prime is a natural number greater than 1 that has no positive divisors other than 1 and itself.';
    return (
      <Form>
        <FormItem
          {...formItemLayout}
          label="Prime between 8 & 12"
          validateStatus={number.validateStatus}
          help={number.errorMsg || tips}
        >
          <InputNumber min={8} max={12} value={number.value} onChange={this.handleNumberChange} />
        </FormItem>
      </Form>
    );
  }
}

ReactDOM.render(<RawForm />, mountNode);

Form 表单 - 图11

自定义校验

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

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

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

  • help:设置校验文案。

import {
  Form,
  Input,
  DatePicker,
  Col,
  TimePicker,
  Select,
  Cascader,
  InputNumber,
} from 'choerodon-ui';

const FormItem = Form.Item;
const Option = Select.Option;

const formItemLayout = {
  labelCol: {
    xs: { span: 24 },
    sm: { span: 5 },
  },
  wrapperCol: {
    xs: { span: 24 },
    sm: { span: 12 },
  },
};

ReactDOM.render(
  <Form>
    <FormItem
      {...formItemLayout}
      label="Fail"
      validateStatus="error"
      help="Should be combination of numbers & alphabets"
    >
      <Input placeholder="unavailable choice" id="error" />
    </FormItem>

    <FormItem {...formItemLayout} label="Warning" validateStatus="warning">
      <Input placeholder="Warning" id="warning" />
    </FormItem>

    <FormItem
      {...formItemLayout}
      label="Validating"
      hasFeedback
      validateStatus="validating"
      help="The information is being validated..."
    >
      <Input placeholder="I'm the content is being validated" id="validating" />
    </FormItem>

    <FormItem {...formItemLayout} label="Success" hasFeedback validateStatus="success">
      <Input placeholder="I'm the content" id="success" />
    </FormItem>

    <FormItem {...formItemLayout} label="Warning" hasFeedback validateStatus="warning">
      <Input placeholder="Warning" id="warning" />
    </FormItem>

    <FormItem
      {...formItemLayout}
      label="Fail"
      hasFeedback
      validateStatus="error"
      help="Should be combination of numbers & alphabets"
    >
      <Input placeholder="unavailable choice" id="error" />
    </FormItem>

    <FormItem {...formItemLayout} label="Success" hasFeedback validateStatus="success">
      <DatePicker style={{ width: '100%' }} />
    </FormItem>

    <FormItem {...formItemLayout} label="Warning" hasFeedback validateStatus="warning">
      <TimePicker style={{ width: '100%' }} />
    </FormItem>

    <FormItem {...formItemLayout} label="Error" hasFeedback validateStatus="error">
      <Select defaultValue="1">
        <Option value="1">Option 1</Option>
        <Option value="2">Option 2</Option>
        <Option value="3">Option 3</Option>
      </Select>
    </FormItem>

    <FormItem
      {...formItemLayout}
      label="Validating"
      hasFeedback
      validateStatus="validating"
      help="The information is being validated..."
    >
      <Cascader defaultValue={['1']} options={[]} />
    </FormItem>

    <FormItem label="inline" {...formItemLayout}>
      <Col span={11}>
        <FormItem validateStatus="error" help="Please select the correct date">
          <DatePicker />
        </FormItem>
      </Col>
      <Col span={2}>
        <span style={{ display: 'inline-block', width: '100%', textAlign: 'center' }}>-</span>
      </Col>
      <Col span={11}>
        <FormItem>
          <DatePicker />
        </FormItem>
      </Col>
    </FormItem>

    <FormItem {...formItemLayout} label="Success" hasFeedback validateStatus="success">
      <InputNumber style={{ width: '100%' }} />
    </FormItem>
  </Form>,
  mountNode,
);

Form 表单 - 图12

表单联动

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

import { Form, Select, Input, Button } from 'choerodon-ui';

const FormItem = Form.Item;
const Option = Select.Option;

class App extends React.Component {
  handleSubmit = e => {
    e.preventDefault();
    this.props.form.validateFields((err, values) => {
      if (!err) {
        console.log('Received values of form: ', values);
      }
    });
  };

  handleSelectChange = value => {
    console.log(value);
    this.props.form.setFieldsValue({
      note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
    });
  };

  render() {
    const { getFieldDecorator } = this.props.form;
    return (
      <Form onSubmit={this.handleSubmit}>
        <FormItem label="Note" labelCol={{ span: 5 }} wrapperCol={{ span: 12 }}>
          {getFieldDecorator('note', {
            rules: [{ required: true, message: 'Please input your note!' }],
          })(<Input />)}
        </FormItem>
        <FormItem label="Gender" labelCol={{ span: 5 }} wrapperCol={{ span: 12 }}>
          {getFieldDecorator('gender', {
            rules: [{ required: true, message: 'Please select your gender!' }],
          })(
            <Select
              placeholder="Select a option and change input text above"
              onChange={this.handleSelectChange}
            >
              <Option value="male">male</Option>
              <Option value="female">female</Option>
            </Select>,
          )}
        </FormItem>
        <FormItem wrapperCol={{ span: 12, offset: 5 }}>
          <Button type="primary" htmlType="submit">
            Submit
          </Button>
        </FormItem>
      </Form>
    );
  }
}

const WrappedApp = Form.create()(App);

ReactDOM.render(<WrappedApp />, mountNode);

Form 表单 - 图13

表单布局

表单有三种布局。

import { Form, Input, Button, Radio } from 'choerodon-ui';

const FormItem = Form.Item;

class FormLayoutDemo extends React.Component {
  constructor() {
    super();
    this.state = {
      formLayout: 'horizontal',
    };
  }

  handleFormLayoutChange = e => {
    this.setState({ formLayout: e.target.value });
  };

  render() {
    const { formLayout } = this.state;
    const formItemLayout =
      formLayout === 'horizontal'
        ? {
            labelCol: { span: 4 },
            wrapperCol: { span: 14 },
          }
        : null;
    const buttonItemLayout =
      formLayout === 'horizontal'
        ? {
            wrapperCol: { span: 14, offset: 4 },
          }
        : null;
    return (
      <div>
        <Form layout={formLayout}>
          <FormItem label="Form Layout" {...formItemLayout}>
            <Radio.Group defaultValue="horizontal" onChange={this.handleFormLayoutChange}>
              <Radio.Button value="horizontal">Horizontal</Radio.Button>
              <Radio.Button value="vertical">Vertical</Radio.Button>
              <Radio.Button value="inline">Inline</Radio.Button>
            </Radio.Group>
          </FormItem>
          <FormItem label="Field A" {...formItemLayout}>
            <Input placeholder="input placeholder" />
          </FormItem>
          <FormItem label="Field B" {...formItemLayout}>
            <Input placeholder="input placeholder" />
          </FormItem>
          <FormItem {...buttonItemLayout}>
            <Button type="primary">Submit</Button>
          </FormItem>
        </Form>
      </div>
    );
  }
}

ReactDOM.render(<FormLayoutDemo />, mountNode);

Form 表单 - 图14

动态校验规则

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

import { Form, Input, Button, Checkbox } from 'choerodon-ui';

const FormItem = Form.Item;

const formItemLayout = {
  labelCol: { span: 4 },
  wrapperCol: { span: 8 },
};
const formTailLayout = {
  labelCol: { span: 4 },
  wrapperCol: { span: 8, offset: 4 },
};
class DynamicRule extends React.Component {
  state = {
    checkNick: false,
  };

  check = () => {
    this.props.form.validateFields(err => {
      if (!err) {
        console.info('success');
      }
    });
  };

  handleChange = e => {
    this.setState(
      {
        checkNick: e.target.checked,
      },
      () => {
        this.props.form.validateFields(['nickname'], { force: true });
      },
    );
  };

  render() {
    const { getFieldDecorator } = this.props.form;
    return (
      <div>
        <FormItem {...formItemLayout} label="Name">
          {getFieldDecorator('username', {
            rules: [
              {
                required: true,
                message: 'Please input your name',
              },
            ],
          })(<Input placeholder="Please input your name" />)}
        </FormItem>
        <FormItem {...formItemLayout} label="Nickname">
          {getFieldDecorator('nickname', {
            rules: [
              {
                required: this.state.checkNick,
                message: 'Please input your nickname',
              },
            ],
          })(<Input placeholder="Please input your nickname" />)}
        </FormItem>
        <FormItem {...formTailLayout}>
          <Checkbox value={this.state.checkNick} onChange={this.handleChange}>
            Nickname is required
          </Checkbox>
        </FormItem>
        <FormItem {...formTailLayout}>
          <Button type="primary" onClick={this.check}>
            Check
          </Button>
        </FormItem>
      </div>
    );
  }
}

const WrappedDynamicRule = Form.create()(DynamicRule);
ReactDOM.render(<WrappedDynamicRule />, mountNode);

Form 表单 - 图15

校验其他组件

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

import {
  Form,
  Select,
  InputNumber,
  Switch,
  Radio,
  Slider,
  Button,
  Upload,
  Icon,
  Rate,
} from 'choerodon-ui';

const FormItem = Form.Item;
const Option = Select.Option;
const RadioButton = Radio.Button;
const RadioGroup = Radio.Group;

class Demo extends React.Component {
  handleSubmit = e => {
    e.preventDefault();
    this.props.form.validateFields((err, values) => {
      if (!err) {
        console.log('Received values of form: ', values);
      }
    });
  };

  normFile = e => {
    console.log('Upload event:', e);
    if (Array.isArray(e)) {
      return e;
    }
    return e && e.fileList;
  };

  render() {
    const { getFieldDecorator } = this.props.form;
    const formItemLayout = {
      labelCol: { span: 6 },
      wrapperCol: { span: 14 },
    };
    return (
      <Form onSubmit={this.handleSubmit}>
        <FormItem {...formItemLayout} label="Plain Text">
          <span className="c7n-form-text">China</span>
        </FormItem>
        <FormItem {...formItemLayout} label="Select" hasFeedback>
          {getFieldDecorator('select', {
            rules: [{ required: true, message: 'Please select your country!' }],
          })(
            <Select placeholder="Please select a country">
              <Option value="china">China</Option>
              <Option value="use">U.S.A</Option>
            </Select>,
          )}
        </FormItem>

        <FormItem {...formItemLayout} label="Select[multiple]">
          {getFieldDecorator('select-multiple', {
            rules: [
              { required: true, message: 'Please select your favourite colors!', type: 'array' },
            ],
          })(
            <Select mode="multiple" placeholder="Please select favourite colors">
              <Option value="red">Red</Option>
              <Option value="green">Green</Option>
              <Option value="blue">Blue</Option>
            </Select>,
          )}
        </FormItem>

        <FormItem {...formItemLayout} label="InputNumber">
          {getFieldDecorator('input-number', { initialValue: 3 })(<InputNumber min={1} max={10} />)}
          <span className="c7n-form-text"> machines</span>
        </FormItem>

        <FormItem {...formItemLayout} label="Switch">
          {getFieldDecorator('switch', { valuePropName: 'checked' })(<Switch />)}
        </FormItem>

        <FormItem {...formItemLayout} label="Slider">
          {getFieldDecorator('slider')(
            <Slider marks={{ 0: 'A', 20: 'B', 40: 'C', 60: 'D', 80: 'E', 100: 'F' }} />,
          )}
        </FormItem>

        <FormItem {...formItemLayout} label="Radio.Group">
          {getFieldDecorator('radio-group')(
            <RadioGroup>
              <Radio value="a">item 1</Radio>
              <Radio value="b">item 2</Radio>
              <Radio value="c">item 3</Radio>
            </RadioGroup>,
          )}
        </FormItem>

        <FormItem {...formItemLayout} label="Radio.Button">
          {getFieldDecorator('radio-button')(
            <RadioGroup>
              <RadioButton value="a">item 1</RadioButton>
              <RadioButton value="b">item 2</RadioButton>
              <RadioButton value="c">item 3</RadioButton>
            </RadioGroup>,
          )}
        </FormItem>

        <FormItem {...formItemLayout} label="Rate">
          {getFieldDecorator('rate', {
            initialValue: 3.5,
          })(<Rate />)}
        </FormItem>

        <FormItem {...formItemLayout} label="Upload" extra="longgggggggggggggggggggggggggggggggggg">
          {getFieldDecorator('upload', {
            valuePropName: 'fileList',
            getValueFromEvent: this.normFile,
          })(
            <Upload name="logo" action="/upload.do" listType="picture">
              <Button>
                <Icon type="upload" /> Click to upload
              </Button>
            </Upload>,
          )}
        </FormItem>

        <FormItem {...formItemLayout} label="Dragger">
          <div className="dropbox">
            {getFieldDecorator('dragger', {
              valuePropName: 'fileList',
              getValueFromEvent: this.normFile,
            })(
              <Upload.Dragger name="files" action="/upload.do">
                <p className="c7n-upload-drag-icon">
                  <Icon type="inbox" />
                </p>
                <p className="c7n-upload-text">Click or drag file to this area to upload</p>
                <p className="c7n-upload-hint">Support for a single or bulk upload.</p>
              </Upload.Dragger>,
            )}
          </div>
        </FormItem>

        <FormItem wrapperCol={{ span: 12, offset: 6 }}>
          <Button type="primary" htmlType="submit">
            Submit
          </Button>
        </FormItem>
      </Form>
    );
  }
}

const WrappedDemo = Form.create()(Demo);

ReactDOM.render(<WrappedDemo />, mountNode);
#components-form-demo-validate-other .dropbox {
  height: 180px;
  line-height: 1.5;
}

API

Form

更多示例参考 rc-form

参数说明类型默认值
formForm.create() 包装过的组件会自带 this.props.form 属性,直接传给 Form 即可。1.7.0 之后无需设置object
hideRequiredMark隐藏所有表单项的必选标记Booleanfalse
layout表单布局(2.8 之后支持)'horizontal'|'vertical'|'inline''horizontal'
onSubmit数据验证成功后回调事件Function(e:Event)

Form.create(options)

使用方式如下:

class CustomizedForm extends React.Component {}

CustomizedForm = Form.create({})(CustomizedForm);

options 的配置项如下。

参数说明类型
mapPropsToFields把父组件的属性映射到表单项上(如:把 Redux store 中的值读出),需要对返回值中的表单域数据用 Form.createFormField 标记(props) => Object{ fieldName: FormField { value } }
validateMessages默认校验信息,可用于把默认错误信息改为中文等,格式与 newMessages 返回值一致Object { nested.path: String }
onFieldsChangeForm.Item 子节点的值发生改变时触发,可以把对应的值转存到 Redux storeFunction(props, fields)
onValuesChange任一表单域的值发生改变时的回调(props, values) => void

经过 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设置一组输入控件的值与 Error。 代码Function({ fieldName: { value: any, errors: Error } })
setFieldsValue设置一组输入控件的值(注意:不要在 componentWillReceiveProps 内使用,否则会导致死循环Function({ fieldName: value }
validateFields校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件Function([fieldNames: string[]], options: object, callback: Function(errors, values, modify))
validateFieldsAndScrollvalidateFields 相似,但校验完后,如果校验不通过的菜单域不在可见范围内,则自动滚动进可见范围参考 validateFields
isModifiedFields判断一组输入控件的值是否有修改(names?: string[]) => boolean
isModifiedField判断一个输入控件的值是否有修改(name: string) => boolean

this.props.form.validateFields/validateFieldsAndScroll([fieldNames: string[]], options: object, callback: Function(errors, values))

参数说明类型默认值
options.first若为 true,则每一表单域的都会在碰到第一个失败了的校验规则后停止校验booleanfalse
options.firstFields指定表单域会在碰到第一个失败了的校验规则后停止校验String[][]
options.force对已经校验过的表单域,在 validateTrigger 再次被触发时是否再次校验booleanfalse
options.scroll定义 validateFieldsAndScroll 的滚动行为,详细配置见 dom-scroll-into-view configObject{}

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 来动态改变表单值。

特别注意

getFieldDecorator(id, options) 参数

参数说明类型默认值
id必填输入控件唯一标志。支持嵌套式的写法string
options.getValueFromEvent可以把 onChange 的参数(如 event)转化为控件的值function(..args)reference
options.initialValue子节点的初始值,类型、可选值均由子节点决定(注意:由于内部校验时使用 === 判断是否变化,建议使用变量缓存所需设置的值而非直接使用字面量))
options.normalize转换默认的 value 给控件,一个选择全部的例子function(value, prevValue, allValues): any-
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 无法自动生成。

  • 2.2.0 之前,只有当表单域为 Form.Item 的子元素时,才会自动生成 help required validateStatus,嵌套情况需要自行设置。

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

校验规则

参数说明类型默认值
enum枚举类型string-
len字段长度number-
max最大长度number-
message校验文案string-
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 中使用

import { Form } from 'choerodon-ui';
import { FormComponentProps } from 'choerodon-ui/lib/form';

interface UserFormProps extends FormComponentProps {
  age: number;
  name: string;
}

class UserForm extends React.Component<UserFormProps, any> {

}