Form 表单

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

表单#

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

  • 水平排列:可以实现 label 标签和表单控件的水平排列;
  • 行内排列:使其表现为 inline-block 级别的控件。

表单域#

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

这里我们分别封装了表单域 <Form.Item /> 和输入控件 <Input />

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

Input 输入框#

<Input {...props} />

API#

Form#

参数 说明 类型 可选值 默认值
horizontal 水平排列布局 boolean false
inline 行内排列布局 boolean false
onSubmit 数据验证成功后回调事件 Function(e:Event)
prefixCls 样式类名,默认为 ant-form,通常您不需要设置 string 'ant-form'

Form.Item#

参数 说明 类型 可选值 默认值
label label 标签的文本 string
labelCol label 标签布局,通 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12} object
wrapperCol 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol object
help 提示信息 string
required 是否必填 bool false
validateStatus 校验状态 string 'success' 'warning' 'error' 'validating'
hasFeedback 配合 validateStatus 属性使用,是否展示校验状态图标 bool false
prefixCls 样式类名,默认为 ant-form,通常您不需要设置 string 'ant-form'

Form.ValueMixin#

Mixin:当表单控件的输入值改变时,更新 formData。

你需要为每个输入控件声明 name 属性

Input#

参数 说明 类型 可选值 默认值
type 【必须】声明 input 类型,同原生 input 标签的 type 属性 string 'text'
value value 值 any
id id number 或 string
size 控件大小,默认值为 default 。注:标准表单内的输入框大小限制为 large。 string {'large','default','small'} 'default'
defaultValue 设置初始默认值 any
disabled 是否禁用状态,默认为 false bool false
addonBefore 带标签的 input,设置前置标签 node
addonAfter 带标签的 input,设置后置标签 node
prefixCls 样式类名前缀,默认是 ant,通常您不需要设置 string 'ant'

Input.Group#

<Input.Group className={string}>      // 样式类名前缀,默认是 ant-input-group,通常您不需要设置。
  {children}
</Input.Group>

代码演示

import {Row, Col, Input} from 'antd';
const InputGroup = Input.Group;

ReactDOM.render(
  <Row>
    <InputGroup>
      <Col span="6">
        <Input id="largeInput" size="large" placeholder="大尺寸" />
      </Col>
      <Col span="6">
        <Input id="defaultInput" placeholder="默认尺寸" />
      </Col>
      <Col span="6">
        <Input id="smallInput" placeholder="小尺寸" size="small" />
      </Col>
    </InputGroup>
  </Row>
, mountNode);

我们为 <Input /> 输入框定义了三种尺寸(大、默认、小),具体使用详见 API

注意: 在表单里面,我们只使用大尺寸, 即高度为 32px,作为唯一的尺寸。

import {Form, Input, Button, Checkbox, message} from 'antd';
const FormItem = Form.Item;

const Demo = React.createClass({
  mixins: [Form.ValueMixin],

  getInitialState() {
    return {
      formData: {
        userName: undefined,
        password: undefined,
        agreement: undefined,
      }
    };
  },

  handleSubmit(e) {
    e.preventDefault();
    message.success('收到表单值~~~ :' + JSON.stringify(this.state.formData, function(k, v) {
      if (typeof v === 'undefined') {
        return '';
      }
      return v;
    }));
  },

  render() {
    const formData = this.state.formData;
    return (
      <Form inline onSubmit={this.handleSubmit}>
        <FormItem
          id="userName"
          label="账户:">
          <Input placeholder="请输入账户名" id="userName" name="userName" onChange={this.setValue.bind(this, 'userName')} value={formData.userName} />
        </FormItem>
        <FormItem
          id="password"
          label="密码:">
          <Input type="password" placeholder="请输入密码" id="password" name="password" onChange={this.setValue.bind(this, 'password')} value={formData.password} />
        </FormItem>
        <FormItem>
          <label className="ant-checkbox-inline">
            <Checkbox name="agreement" value={formData.agreement} onChange={this.setValue.bind(this, 'agreement')} /> 记住我
          </label>
        </FormItem>
        <Button type="primary" htmlType="submit">登录</Button>
      </Form>
    );
  }
});

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

行内排列,常用于登录界面。

import {Form, Input, Button, Checkbox, Radio, Row, Col, message} from 'antd';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;

const Demo = React.createClass({
  mixins: [Form.ValueMixin],

  getInitialState() {
    return {
      formData: {
        userName: '大眼萌 minion',
        password: undefined,
        gender: 'male',
        remark: undefined,
        agreement: undefined,
      }
    };
  },

  handleSubmit(e) {
    e.preventDefault();
    message.success('收到表单值~~~ :' + JSON.stringify(this.state.formData, function(k, v) {
      if (typeof v === 'undefined') {
        return '';
      }
      return v;
    }));
  },

  render() {
    const formData = this.state.formData;
    return (
      <Form horizontal onSubmit={this.handleSubmit}>
        <FormItem
          label="用户名:"
          labelCol={{span: 6}}
          wrapperCol={{span: 6}}
          required>
          <p className="ant-form-text" id="userName" name="userName">大眼萌 minion</p>
        </FormItem>
        <FormItem
          id="password"
          label="密码:"
          labelCol={{span: 6}}
          wrapperCol={{span: 14}}
          required>
          <Input type="password" id="password" name="password" placeholder="请输入密码" value={formData.password} onChange={this.setValue.bind(this, 'password')} />
        </FormItem>
        <FormItem
          label="您的性别:"
          labelCol={{span: 6}}
          wrapperCol={{span: 14}}
          required>
            <RadioGroup name="gender" value={formData.gender} onChange={this.setValue.bind(this, 'gender')} >
              <Radio value="male">男的</Radio>
              <Radio value="female">女的</Radio>
            </RadioGroup>
        </FormItem>
        <FormItem
          id="remark"
          label="备注:"
          labelCol={{span: 6}}
          wrapperCol={{span: 14}}
          required
          help="随便写点什么">
          <Input type="textarea" placeholder="随便写" id="remark" name="remark" value={formData.remark} onChange={this.setValue.bind(this, 'remark')} />
        </FormItem>
        <FormItem
          wrapperCol={{span: 14, offset: 6}} >
          <label>
            <Checkbox name="agreement" value={formData.agreement} onChange={this.setValue.bind(this, 'agreement')} /> 同意
          </label>
        </FormItem>
        <Row>
          <Col span="16" offset="6">
            <Button type="primary" htmlType="submit">确定</Button>
          </Col>
        </Row>
      </Form>
    );
  }
});

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

示例展示了如何通过使用 Form.ValueMixin 来获取和更新表单提交的数值。

注意: 1)需要为每个输入控件声明 name 属性;2)ES6 语法 不支持 mixins

import {Form, Input, Select, Checkbox, Radio} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const RadioGroup = Radio.Group;

function handleSelectChange(value) {
  console.log('selected ' + value);
}

ReactDOM.render(
  <Form horizontal>
    <FormItem
      id="control-input"
      label="输入框:"
      labelCol={{span: 6}}
      wrapperCol={{span: 14}}>
      <Input id="control-input" placeholder="Please enter..." />
    </FormItem>

    <FormItem
      id="control-textarea"
      label="文本域:"
      labelCol={{span: 6}}
      wrapperCol={{span: 14}}>
      <Input type="textarea" id="control-textarea" rows="3" />
    </FormItem>

    <FormItem
      id="select"
      label="Select 选择器:"
      labelCol={{span: 6}}
      wrapperCol={{span: 14}}>
      <Select id="select" size="large" defaultValue="lucy" style={{width:200}} onChange={handleSelectChange}>
        <Option value="jack">jack</Option>
        <Option value="lucy">lucy</Option>
        <Option value="disabled" disabled>disabled</Option>
        < Option value="yiminghe">yiminghe</Option>
      </Select>
    </FormItem>

    <FormItem
      label="Checkbox 多选框:"
      labelCol={{span: 6}}
      wrapperCol={{span: 18}} >
        <label className="ant-checkbox-vertical">
          <Checkbox />选项一
        </label>
        <label className="ant-checkbox-vertical">
          <Checkbox />选项二
        </label>
        <label className="ant-checkbox-vertical">
          <Checkbox disabled />选项三(不可选)
        </label>
    </FormItem>

    <FormItem
      label="Checkbox 多选框:"
      labelCol={{span: 6}}
      wrapperCol={{span: 18}} >
        <label className="ant-checkbox-inline">
          <Checkbox />选项一
        </label>
        <label className="ant-checkbox-inline">
          <Checkbox />选项二
        </label>
        <label className="ant-checkbox-inline">
          <Checkbox />选项三
        </label>
    </FormItem>

    <FormItem
      label="Radio 单选框:"
      labelCol={{span: 6}}
      wrapperCol={{span: 18}} >
        <RadioGroup value="b">
          <Radio value="a">A</Radio>
          <Radio value="b">B</Radio>
          <Radio value="c">C</Radio>
          <Radio value="d">D</Radio>
        </RadioGroup>
    </FormItem>
  </Form>
, mountNode);

展示所有支持的表单控件。

: 输入框:只有正确设置了 type 属性的输入控件才能被赋予正确的样式。

import {Form, Input, Select, Row, Col} from 'antd';
const FormItem = Form.Item;
const InputGroup = Input.Group;
const Option = Select.Option;

ReactDOM.render(
  <Form horizontal>
    <FormItem
      label="标签输入框:"
      labelCol={{span: 6}}
      wrapperCol={{span: 16}}>
      <Input addonBefore="Http://" defaultValue="mysite.com" id="site1"/>
    </FormItem>

    <FormItem
      label="标签输入框:"
      labelCol={{span: 6}}
      wrapperCol={{span: 16}}>
      <Input addonBefore="Http://" addonAfter=".com" defaultValue="mysite" id="site2"/>
    </FormItem>

    <FormItem
      label="select 标签输入框:"
      labelCol={{span: 6}}
      wrapperCol={{span: 16}}>
      <InputGroup>
        <Input id="site4" placeholder="www.mysite" />
        <div className="ant-input-group-wrap">
          <Select defaultValue=".com" style={{width:70}}>
            <Option value=".com">.com</Option>
            <Option value=".jp">.jp</Option>
            <Option value=".cn">.cn</Option>
            <Option value=".org">.org</Option>
          </Select>
        </div>
      </InputGroup>
    </FormItem>

    <FormItem
      label="输入身份证:"
      labelCol={{span: 6}}
      wrapperCol={{span: 16}}>
      <InputGroup>
        <Col span="6">
          <Input id="certNo1" />
        </Col>
        <Col span="6">
          <Input id="certNo2" />
        </Col>
        <Col span="6">
          <Input id="certNo3" />
        </Col>
        <Col span="6">
          <Input id="certNo4" />
        </Col>
      </InputGroup>
    </FormItem>

    <FormItem
      label="电话号码:"
      labelCol={{span: 6}}
      wrapperCol={{span: 16}}>
      <Row>
        <Col span="4">
          <Input id="tel1" defaultValue="086" />
        </Col>
        <Col span="2">
          <p className="ant-form-split">--</p>
        </Col>
        <Col span="18">
          <InputGroup>
            <Col span="8">
              <Input id="tel1" />
            </Col>
            <Col span="8">
              <Input id="tel2" />
            </Col>
            <Col span="8">
              <Input id="tel3" />
            </Col>
          </InputGroup>
        </Col>
      </Row>
    </FormItem>
  </Form>

, mountNode);

各类输入框的组合展现。

import { Form, Select, InputNumber, DatePicker, Switch,
         Slider, Button, message, Row, Col, Upload, Icon } from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;

const Demo = React.createClass({
  mixins: [Form.ValueMixin],

  getInitialState() {
    return {
      formData: {
        inputNumber: undefined,
        static: '唧唧复唧唧木兰当户织呀',
        switch: undefined,
        slider: undefined,
        select: undefined,
        startDate: undefined,
        endDate: undefined,
      }
    };
  },

  handleUpload(info) {
    if (info.file.status !== 'uploading') {
      console.log(info.file, info.fileList);
    }
    if (info.file.status === 'done') {
      message.success(info.file.name + ' 上传成功。');
    } else if (info.file.status === 'error') {
      message.error(info.file.name + ' 上传失败。');
    }
  },

  handleSubmit(e) {
    e.preventDefault();
    message.success('收到表单值~~~ :' + JSON.stringify(this.state.formData, function(k, v) {
      if (typeof v === 'undefined') {
        return '';
      }
      return v;
    }));
  },

  render() {
    const formData = this.state.formData;
    return (
      <Form horizontal onSubmit={this.handleSubmit} >
        <FormItem
          label="InputNumber 数字输入框:"
          labelCol={{span: 8}}
          wrapperCol={{span: 10}}
          required>
          <InputNumber size="large" min={1} max={10} style={{width:100}} defaultValue={3} name="inputNumber" onChange={this.setValue.bind(this, 'inputNumber')} value={formData.inputNumber} />
          <span className="ant-form-text"> 台机器</span>
        </FormItem>

        <FormItem
          label="我是标题:"
          labelCol={{span: 8}}
          wrapperCol={{span: 10}}
          required>
          <p className="ant-form-text" id="static" name="static">唧唧复唧唧木兰当户织呀</p>
          <p className="ant-form-text">
            <a href="#">链接文字</a>
          </p>
        </FormItem>

        <FormItem
          label="Switch 开关:"
          labelCol={{span: 8}}
          wrapperCol={{span: 10}}
          required>
          <Switch name="switch" onChange={this.setValue.bind(this, 'switch')} value={formData.switch} />
        </FormItem>

        <FormItem
          label="Slider 滑动输入条:"
          labelCol={{span: 8}}
          wrapperCol={{span: 10}}
          required>
          <Slider marks={['A', 'B', 'C', 'D', 'E', 'F', 'G']} name="slider" onChange={this.setValue.bind(this, 'slider')} />
        </FormItem>

        <FormItem
          label="Select 选择器:"
          labelCol={{span: 8}}
          wrapperCol={{span: 16}}
          required>
          <Select size="large" defaultValue="lucy" style={{width:200}} name="select" onChange={this.setValue.bind(this, 'select')} value={formData.select}>
            <Option value="jack">jack</Option>
            <Option value="lucy">lucy</Option>
            <Option value="disabled" disabled>disabled</Option>
            <Option value="yiminghe">yiminghe</Option>
          </Select>
        </FormItem>

        <FormItem
          label="DatePicker 日期选择框:"
          labelCol={{span: 8}}
          required>
          <Col span="6">
            <DatePicker name="startDate" onChange={this.setValue.bind(this, 'startDate')} value={formData.startDate} />
          </Col>
          <Col span="1">
            <p className="ant-form-split">-</p>
          </Col>
          <Col span="6">
            <DatePicker name="endDate" onChange={this.setValue.bind(this, 'endDate')} value={formData.endDate} />
          </Col>
        </FormItem>
        <FormItem
          label="logo图:"
          labelCol={{span: 8}}
          wrapperCol={{span: 16}}
          help="提示信息要长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长">
          <Upload name="logo" action="/upload.do" listType="picture" onChange={this.handleUpload}>
            <Button type="ghost">
              <Icon type="upload" /> 点击上传
            </Button>
          </Upload>
        </FormItem>
        <Row>
          <Col span="16" offset="8">
            <Button type="primary" htmlType="submit">确定</Button>
          </Col>
        </Row>
      </Form>
    );
  }
});

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

集中营,展示和表单相关的其他 ant-design 组件。

import { Form, Input, DatePicker, Col } from 'antd';
const FormItem = Form.Item;

ReactDOM.render(
  <Form horizontal>
    <FormItem
      label="失败校验:"
      labelCol={{span: 5}}
      wrapperCol={{span: 12}}
      validateStatus="error"
      help="请输入数字和字母组合">
      <Input defaultValue="无效选择" id="error" />
    </FormItem>

    <FormItem
      label="警告校验:"
      labelCol={{span: 5}}
      wrapperCol={{span: 12}}
      validateStatus="warning">
      <Input defaultValue="前方高能预警" id="warning" />
    </FormItem>

    <FormItem
      label="校验中:"
      labelCol={{span: 5}}
      wrapperCol={{span: 12}}
      hasFeedback
      validateStatus="validating"
      help="信息审核中...">
      <Input defaultValue="我是被校验的内容" id="validating" />
    </FormItem>

    <FormItem
      label="成功校验:"
      labelCol={{span: 5}}
      wrapperCol={{span: 12}}
      hasFeedback
      validateStatus="success">
      <Input defaultValue="我是正文" id="success" />
    </FormItem>

    <FormItem
      label="警告校验:"
      labelCol={{span: 5}}
      wrapperCol={{span: 12}}
      hasFeedback
      validateStatus="warning">
      <Input defaultValue="前方高能预警" id="warning" />
    </FormItem>

    <FormItem
      label="失败校验:"
      labelCol={{span: 5}}
      wrapperCol={{span: 12}}
      hasFeedback
      validateStatus="error"
      help="请输入数字和字母组合">
      <Input defaultValue="无效选择" id="error" />
    </FormItem>

    <FormItem
      label="Datepicker:"
      labelCol={{span: 5}}
      validateStatus="error"
      help>
      <Col span="6">
        <DatePicker />
      </Col>
      <Col span="1">
        <p className="ant-form-split">-</p>
      </Col>
      <Col span="6">
        <DatePicker />
      </Col>
      <Col span="19" offset="5">
        <p className="ant-form-explain">请输入正确选项</p>
      </Col>
    </FormItem>

    <FormItem
      label="Datepicker:"
      labelCol={{span: 5}}>
      <Col span="6">
        <FormItem validateStatus="error">
          <DatePicker />
          <p className="ant-form-explain">请输入正确选项</p>
        </FormItem>
      </Col>
      <Col span="1">
        <p className="ant-form-split">-</p>
      </Col>
      <Col span="6">
        <FormItem>
          <DatePicker />
        </FormItem>
      </Col>
    </FormItem>
  </Form>
, mountNode);

我们为表单控件定义了三种校验状态,为 <FormItem> 定义 validateStatus 属性即可。

validateStatus: ['success', 'warning', 'error', 'validating']。

另外为输入框添加反馈图标,设置 <FormItem>hasFeedback 属性值为 true 即可。

注意: 反馈图标只对 <Input /> 有效。

import {Row, Col, Button, Input, Form} from 'antd';
const FormItem = Form.Item;

ReactDOM.render(
  <Form horizontal>
    <FormItem
      label="单独禁用输入框:"
      labelCol={{span:5}}
      wrapperCol={{span:12}}>
      <Input defaultValue="我是禁用的" disabled />
    </FormItem>

    <fieldset disabled>
      <legend>禁用的 fieldset</legend>
      <FormItem
        id="userName"
        label="用户名:"
        labelCol={{span:5}}
        wrapperCol={{span:12}}
        required>
        <p className="ant-form-text">大眼萌 minion</p>
      </FormItem>
      <FormItem
        id="password"
        label="密码:"
        labelCol={{span:5}}
        wrapperCol={{span:12}}
        required>
        <Input type="password" defaultValue="123456" id="password" />
      </FormItem>
      <Row>
        <Col span="12" offset="5">
          <Button htmlType="submit" type="primary">确定</Button>
        </Col>
      </Row>
    </fieldset>
  </Form>
, mountNode);

1) 单独为输入控件设置 disabled 属性;

2) 为 <fieldset> 设置 disabled 属性,可以禁用 <fieldset> 中包含的所有控件。

import { Form, Input, Row, Col, Button } from 'antd';
const FormItem = Form.Item;

ReactDOM.render(
<Form horizontal className="advanced-search-form">
  <Row>
    <Col span="8">
      <FormItem
        label="搜索名称:"
        labelCol={{span: 10}}
        wrapperCol={{span: 14}}>
        <Input placeholder="请输入搜索名称" />
      </FormItem>
      <FormItem
        label="较长搜索名称:"
        labelCol={{span: 10}}
        wrapperCol={{span: 14}}>
        <Input placeholder="请输入搜索名称" />
      </FormItem>
      <FormItem
        label="搜索名称:"
        labelCol={{span: 10}}
        wrapperCol={{span: 14}}>
        <Input placeholder="请输入搜索名称" />
      </FormItem>
    </Col>
    <Col span="8">
      <FormItem
        label="搜索名称:"
        labelCol={{span: 10}}
        wrapperCol={{span: 14}}>
        <Input placeholder="请输入搜索名称" />
      </FormItem>
      <FormItem
        label="较长搜索名称:"
        labelCol={{span: 10}}
        wrapperCol={{span: 14}}>
        <Input placeholder="请输入搜索名称" />
      </FormItem>
      <FormItem
        label="搜索名称:"
        labelCol={{span: 10}}
        wrapperCol={{span: 14}}>
        <Input placeholder="请输入搜索名称" />
      </FormItem>
    </Col>
    <Col span="8">
      <FormItem
        label="搜索名称:"
        labelCol={{span: 10}}
        wrapperCol={{span: 14}}>
        <Input placeholder="请输入搜索名称" />
      </FormItem>
      <FormItem
        label="较长搜索名称:"
        labelCol={{span: 10}}
        wrapperCol={{span: 14}}>
        <Input placeholder="请输入搜索名称" />
      </FormItem>
      <FormItem
        label="搜索名称:"
        labelCol={{span: 10}}
        wrapperCol={{span: 14}}>
        <Input placeholder="请输入搜索名称" />
      </FormItem>
    </Col>
  </Row>
  <Row>
    <Col span="8" offset="16" style={{textAlign: 'right'}}>
      <Button type="primary" htmlType="submit">搜索</Button>
      <Button type="ghost">清除条件</Button>
    </Col>
  </Row>
</Form>
, mountNode);
/* 定制样式 */

.advanced-search-form {
  padding: 16px 8px;
  background: #f8f8f8;
  border: 1px solid #d9d9d9;
  border-radius: 6px;
}

/* 由于输入标签长度不确定,所以需要微调使之看上去居中 */
.advanced-search-form > .row {
  margin-left: -10px;
}

.advanced-search-form > .row > .col-8 {
  padding: 0 8px;
}

.advanced-search-form .ant-form-item {
  margin-bottom: 16px;
}

.advanced-search-form .ant-btn + .ant-btn {
  margin-left: 8px;
}

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

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

import { Icon, Input, Button } from 'antd';
import classNames from 'classnames';
const InputGroup = Input.Group;

const SearchInput = React.createClass({
  getInitialState() {
    return {
      value: '',
      focus: false
    };
  },
  handleInputChange(e) {
    this.setState({
      value: e.target.value,
    });
  },
  handleFocusBlur(e) {
    this.setState({
      focus: e.target === document.activeElement,
    });
  },
  handleSearch() {
    if (this.props.onSearch) {
      this.props.onSearch();
    }
  },
  render() {
    const btnCls = classNames({
      'ant-search-btn': true,
      'ant-search-btn-noempty': !!this.state.value.trim(),
    });
    const searchCls = classNames({
      'ant-search-input': true,
      'ant-search-input-focus': this.state.focus,
    });
    return <InputGroup className={searchCls} style={this.props.style}>
      <Input {...this.props} value={this.state.value} onChange={this.handleInputChange}
        onFocus={this.handleFocusBlur} onBlur={this.handleFocusBlur} />
      <div className="ant-input-group-wrap">
        <Button className={btnCls} onClick={this.handleSearch}>
          <Icon type="search" />
        </Button>
      </div>
    </InputGroup>;
  }
});

ReactDOM.render(
  <SearchInput placeholder="input search text" style={{width: 200}} />
, mountNode);

带有搜索按钮。