Form表单
具有数据收集、校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。
表单#
我们为 form
提供了以下三种排列方式:
水平排列:标签和表单控件水平排列;(默认)
垂直排列:标签和表单控件上下垂直排列;
行内排列:表单项水平行内排列。
表单域#
表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等。
这里我们封装了表单域 <Form.Item />
。
<Form.Item {...props}>
{children}
</Form.Item>
注:标准表单中一律使用大号控件。
代码演示
import { Form, Icon, Input, Button } from 'antd';
const FormItem = Form.Item;
function hasErrors(fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field]);
}
class HorizontalLoginForm extends React.Component {
componentDidMount() {
// To disabled submit button at the beginning.
this.props.form.validateFields();
}
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
}
render() {
const { getFieldDecorator, getFieldsError, getFieldError, isFieldTouched } = this.props.form;
// Only show error after a field is touched.
const userNameError = isFieldTouched('userName') && getFieldError('userName');
const passwordError = isFieldTouched('password') && getFieldError('password');
return (
<Form layout="inline" onSubmit={this.handleSubmit}>
<FormItem
validateStatus={userNameError ? 'error' : ''}
help={userNameError || ''}
>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ fontSize: 13 }} />} placeholder="Username" />
)}
</FormItem>
<FormItem
validateStatus={passwordError ? 'error' : ''}
help={passwordError || ''}
>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ fontSize: 13 }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button
type="primary"
htmlType="submit"
disabled={hasErrors(getFieldsError())}
>
Log in
</Button>
</FormItem>
</Form>
);
}
}
const WrappedHorizontalLoginForm = Form.create()(HorizontalLoginForm);
ReactDOM.render(<WrappedHorizontalLoginForm />, mountNode);
import { Form, Icon, Input, Button, Checkbox } from 'antd';
const FormItem = Form.Item;
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ fontSize: 13 }} />} placeholder="Username" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ fontSize: 13 }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true,
})(
<Checkbox>Remember me</Checkbox>
)}
<a className="login-form-forgot" href="">Forgot password</a>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
Or <a href="">register now!</a>
</FormItem>
</Form>
);
}
}
const WrappedNormalLoginForm = Form.create()(NormalLoginForm);
ReactDOM.render(<WrappedNormalLoginForm />, mountNode);
#components-form-demo-normal-login .login-form {
max-width: 300px;
}
#components-form-demo-normal-login .login-form-forgot {
float: right;
}
#components-form-demo-normal-login .login-form-button {
width: 100%;
}
import { Form, Input, Tooltip, Icon, Cascader, Select, Row, Col, Checkbox, Button, AutoComplete } from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const AutoCompleteOption = AutoComplete.Option;
const residences = [{
value: 'zhejiang',
label: 'Zhejiang',
children: [{
value: 'hangzhou',
label: 'Hangzhou',
children: [{
value: 'xihu',
label: 'West Lake',
}],
}],
}, {
value: 'jiangsu',
label: 'Jiangsu',
children: [{
value: 'nanjing',
label: 'Nanjing',
children: [{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
}],
}],
}];
class RegistrationForm extends React.Component {
state = {
confirmDirty: false,
autoCompleteResult: [],
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
}
handleConfirmBlur = (e) => {
const value = e.target.value;
this.setState({ confirmDirty: this.state.confirmDirty || !!value });
}
checkPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && value !== form.getFieldValue('password')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
}
}
checkConfirm = (rule, value, callback) => {
const form = this.props.form;
if (value && this.state.confirmDirty) {
form.validateFields(['confirm'], { force: true });
}
callback();
}
handleWebsiteChange = (value) => {
let autoCompleteResult;
if (!value) {
autoCompleteResult = [];
} else {
autoCompleteResult = ['.com', '.org', '.net'].map(domain => `${value}${domain}`);
}
this.setState({ autoCompleteResult });
}
render() {
const { getFieldDecorator } = this.props.form;
const { autoCompleteResult } = this.state;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 14,
offset: 6,
},
},
};
const prefixSelector = getFieldDecorator('prefix', {
initialValue: '86',
})(
<Select style={{ width: 60 }}>
<Option value="86">+86</Option>
<Option value="87">+87</Option>
</Select>
);
const websiteOptions = autoCompleteResult.map(website => (
<AutoCompleteOption key={website}>{website}</AutoCompleteOption>
));
return (
<Form onSubmit={this.handleSubmit}>
<FormItem
{...formItemLayout}
label="E-mail"
hasFeedback
>
{getFieldDecorator('email', {
rules: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}],
})(
<Input />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Password"
hasFeedback
>
{getFieldDecorator('password', {
rules: [{
required: true, message: 'Please input your password!',
}, {
validator: this.checkConfirm,
}],
})(
<Input type="password" />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Confirm Password"
hasFeedback
>
{getFieldDecorator('confirm', {
rules: [{
required: true, message: 'Please confirm your password!',
}, {
validator: this.checkPassword,
}],
})(
<Input type="password" onBlur={this.handleConfirmBlur} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label={(
<span>
Nickname
<Tooltip title="What do you want other to call you?">
<Icon type="question-circle-o" />
</Tooltip>
</span>
)}
hasFeedback
>
{getFieldDecorator('nickname', {
rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }],
})(
<Input />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Habitual Residence"
>
{getFieldDecorator('residence', {
initialValue: ['zhejiang', 'hangzhou', 'xihu'],
rules: [{ type: 'array', required: true, message: 'Please select your habitual residence!' }],
})(
<Cascader options={residences} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Phone Number"
>
{getFieldDecorator('phone', {
rules: [{ required: true, message: 'Please input your phone number!' }],
})(
<Input addonBefore={prefixSelector} style={{ width: '100%' }} />
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Website"
>
{getFieldDecorator('website', {
rules: [{ required: true, message: 'Please input website!' }],
})(
<AutoComplete
dataSource={websiteOptions}
onChange={this.handleWebsiteChange}
placeholder="website"
>
<Input />
</AutoComplete>
)}
</FormItem>
<FormItem
{...formItemLayout}
label="Captcha"
extra="We must make sure that your are a human."
>
<Row gutter={8}>
<Col span={12}>
{getFieldDecorator('captcha', {
rules: [{ required: true, message: 'Please input the captcha you got!' }],
})(
<Input size="large" />
)}
</Col>
<Col span={12}>
<Button size="large">Get captcha</Button>
</Col>
</Row>
</FormItem>
<FormItem {...tailFormItemLayout} style={{ marginBottom: 8 }}>
{getFieldDecorator('agreement', {
valuePropName: 'checked',
})(
<Checkbox>I have read the <a href="">agreement</a></Checkbox>
)}
</FormItem>
<FormItem {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">Register</Button>
</FormItem>
</Form>
);
}
}
const WrappedRegistrationForm = Form.create()(RegistrationForm);
ReactDOM.render(<WrappedRegistrationForm />, mountNode);
import { Form, Row, Col, Input, Button, Icon } from 'antd';
const FormItem = Form.Item;
class AdvancedSearchForm extends React.Component {
state = {
expand: false,
};
handleSearch = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
console.log('Received values of form: ', values);
});
}
handleReset = () => {
this.props.form.resetFields();
}
toggle = () => {
const { expand } = this.state;
this.setState({ expand: !expand });
}
// To generate mock Form.Item
getFields() {
const count = this.state.expand ? 10 : 6;
const { getFieldDecorator } = this.props.form;
const formItemLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 19 },
};
const children = [];
for (let i = 0; i < 10; i++) {
children.push(
<Col span={8} key={i} style={{ display: i < count ? 'block' : 'none' }}>
<FormItem {...formItemLayout} label={`Field ${i}`}>
{getFieldDecorator(`field-${i}`)(
<Input placeholder="placeholder" />
)}
</FormItem>
</Col>
);
}
return children;
}
render() {
return (
<Form
className="ant-advanced-search-form"
onSubmit={this.handleSearch}
>
<Row gutter={40}>{this.getFields()}</Row>
<Row>
<Col span={24} style={{ textAlign: 'right' }}>
<Button type="primary" htmlType="submit">Search</Button>
<Button style={{ marginLeft: 8 }} onClick={this.handleReset}>
Clear
</Button>
<a style={{ marginLeft: 8, fontSize: 12 }} onClick={this.toggle}>
Collapse <Icon type={this.state.expand ? 'up' : 'down'} />
</a>
</Col>
</Row>
</Form>
);
}
}
const WrappedAdvancedSearchForm = Form.create()(AdvancedSearchForm);
ReactDOM.render(
<div>
<WrappedAdvancedSearchForm />
<div className="search-result-list">Search Result List</div>
</div>,
mountNode
);
#components-form-demo-advanced-search .ant-advanced-search-form {
padding: 24px;
background: #fbfbfb;
border: 1px solid #d9d9d9;
border-radius: 6px;
}
import { Button, Modal, Form, Input, Radio } from 'antd';
const FormItem = Form.Item;
const CollectionCreateForm = Form.create()(
(props) => {
const { visible, onCancel, onCreate, form } = 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.form;
form.validateFields((err, values) => {
if (err) {
return;
}
console.log('Received values of form: ', values);
form.resetFields();
this.setState({ visible: false });
});
}
saveFormRef = (form) => {
this.form = form;
}
render() {
return (
<div>
<Button type="primary" onClick={this.showModal}>New Collection</Button>
<CollectionCreateForm
ref={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;
}
import { Form, Input, Icon, Button } from 'antd';
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 = () => {
uuid++;
const { form } = this.props;
// can use data-binding to get
const keys = form.getFieldValue('keys');
const nextKeys = keys.concat(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 .3s;
}
.dynamic-delete-button:hover {
color: #777;
}
.dynamic-delete-button[disabled] {
cursor: not-allowed;
opacity: 0.5;
}
import { Form, Input, Select, Button } from 'antd';
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(Object.assign({}, 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%' }}
/>
<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);
{ "username": { "value": "benjycui" } }
import { Form, Input } from 'antd';
const FormItem = Form.Item;
const CustomizedForm = Form.create({
onFieldsChange(props, changedFields) {
props.onChange(changedFields);
},
mapPropsToFields(props) {
return {
username: {
...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: { ...this.state.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);
import { Form, InputNumber } from 'antd';
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);
import { Form, Input, DatePicker, Col, TimePicker, Select, Cascader, InputNumber } from 'antd';
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);
import { Form, Select, Input, Button } from 'antd';
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: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('note', {
rules: [{ required: true, message: 'Please input your note!' }],
})(
<Input />
)}
</FormItem>
<FormItem
label="Gender"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{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: 8, offset: 4 }}
>
<Button type="primary" htmlType="submit">
Submit
</Button>
</FormItem>
</Form>
);
}
}
const WrappedApp = Form.create()(App);
ReactDOM.render(<WrappedApp />, mountNode);
import { Form, Input, Button, Radio } from 'antd';
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);
import { Form, Input, Button, Checkbox } from 'antd';
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);
import {
Form, Select, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon,
} from 'antd';
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="ant-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="ant-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="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="ant-upload-drag-icon">
<Icon type="inbox" />
</p>
<p className="ant-upload-text">Click or drag file to this area to upload</p>
<p className="ant-upload-hint">Support for a single or bulk upload. Strictly prohibit from uploading company data or other band files</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。
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
form | 经 Form.create() 包装过的组件会自带 this.props.form 属性,直接传给 Form 即可。1.7.0 之后无需设置 | object | 无 |
layout | 表单布局(2.8 之后支持) | 'horizontal'|'vertical'|'inline' | 'horizontal' |
horizontal | 水平排列布局(2.8 之后废弃) | boolean | true |
vertical | 垂直排列布局(2.8 之后废弃) | boolean | false |
inline | 行内排列布局(2.8 之后废弃) | boolean | false |
onSubmit | 数据验证成功后回调事件 | Function(e:Event) | |
hideRequiredMark | 隐藏所有表单项的必选标记 | Boolean | false |
Form.create(options)#
使用方式如下:
class CustomizedForm extends React.Component {}
CustomizedForm = Form.create({})(CustomizedForm);
options
的配置项如下。
参数 | 说明 | 类型 |
---|---|---|
onFieldsChange | 当 Form.Item 子节点的值发生改变时触发,可以把对应的值转存到 Redux store | Function(props, fields) |
mapPropsToFields | 把父组件的属性映射到表单项上(可用于把 Redux store 中的值读出) | Function(props): Object{ fieldName: Object{ value } } |
onValuesChange | 任一表单域的值发生改变时的回调 | (props, values) => void |
经过 Form.create
包装的组件将会自带 this.props.form
属性,this.props.form
提供的 API 如下:
注意:使用
getFieldsValue
getFieldValue
setFieldsValue
等时,应确保对应的 field 已经用getFieldDecorator
注册过了。
方法 | 说明 | 类型 |
---|---|---|
getFieldsValue | 获取一组输入控件的值,如不传入参数,则获取全部组件的值 | Function([fieldNames: string[]]) |
getFieldValue | 获取一个输入控件的值 | Function(fieldName: string) |
setFieldsValue | 设置一组输入控件的值(注意:不要在 componentWillReceiveProps 内使用,否则会导致死循环,更多) | Function({ fieldName: value } |
setFields | 设置一组输入控件的值与 Error。 代码 | Function({ fieldName: { value: any, errors: Error } }) |
validateFields | 校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件 | Function([fieldNames: string[]], options: object, callback: Function(errors, values)) |
validateFieldsAndScroll | 与 validateFields 相似,但校验完后,如果校验不通过的菜单域不在可见范围内,则自动滚动进可见范围 | 参考 validateFields |
getFieldError | 获取某个输入控件的 Error | Function(name) |
getFieldsError | 获取一组输入控件的 Error ,如不传入参数,则获取全部组件的 Error | Function([names: string[]]) |
isFieldValidating | 判断一个输入控件是否在校验状态 | Function(name) |
isFieldTouched | 判断一个输入控件是否经历过 getFieldDecorator 的值收集时机 options.trigger | (name: string) => boolean |
isFieldsTouched | 判断是否任一输入控件经历过 getFieldDecorator 的值收集时机 options.trigger | (names?: string[]) => boolean |
resetFields | 重置一组输入控件的值(为 initialValue )与状态,如不传入参数,则重置所有组件 | Function([names: string[]]) |
getFieldDecorator | 用于和表单进行双向绑定,详见下方描述 |
this.props.form.validateFields/validateFieldsAndScroll([fieldNames: string[]], options: object, callback: Function(errors, values))#
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
options.first | 若为 true,则每一表单域的都会在碰到第一个失败了的校验规则后停止校验 | boolean | false |
options.firstFields | 指定表单域会在碰到第一个失败了的校验规则后停止校验 | String[] | [] |
options.force | 对已经校验过的表单域,在 validateTrigger 再次被触发时是否再次校验 | boolean | false |
options.scroll | 定义 validateFieldsAndScroll 的滚动行为,详细配置见 dom-scroll-into-view config | Object | {} |
this.props.form.getFieldDecorator(id, options)#
经过 getFieldDecorator
包装的控件,表单控件会自动添加 value
(或 valuePropName
指定的其他属性) onChange
(或 trigger
指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
你不再需要也不应该用
onChange
来做同步,但还是可以继续监听onChange
等事件。你不能用控件的
value
defaultValue
等属性来设置表单域的值,默认值可以用getFieldDecorator
里的initialValue
。你不应该用
setState
,可以使用this.props.form.setFieldsValue
来动态改变表单值。
特别注意#
getFieldDecorator
不能用于装饰纯函数组件。如果使用的是
react@<15.3.0
,则getFieldDecorator
调用不能位于纯函数组件中: https://github.com/facebook/react/pull/6534
getFieldDecorator(id, options) 参数#
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
id | 必填输入控件唯一标志。支持嵌套式的写法。 | string | |
options.valuePropName | 子节点的值的属性,如 Switch 的是 'checked' | string | 'value' |
options.initialValue | 子节点的初始值,类型、可选值均由子节点决定(注意:由于内部校验时使用 === 判断是否变化,建议使用变量缓存所需设置的值而非直接使用字面量)) | ||
options.trigger | 收集子节点的值的时机 | string | 'onChange' |
options.getValueFromEvent | 可以把 onChange 的参数(如 event)转化为控件的值 | function(..args) | reference |
options.validateTrigger | 校验子节点值的时机 | string|string[] | 'onChange' |
options.rules | 校验规则,参考下方文档 | object[] | |
options.validateFirst | 当某一规则校验不通过时,是否停止剩下的规则的校验 | boolean | false |
options.exclusive | 是否和其他控件互斥,特别用于 Radio 单选控件 | boolean | false |
options.normalize | 转换默认的 value 给控件,一个选择全部的例子 | function(value, prevValue, allValues): any | - |
更多参数请查看 rc-form option。
Form.Item#
注意:
一个 Form.Item 建议只放一个被 getFieldDecorator 装饰过的 child,当有多个被装饰过的 child 时,
help
required
validateStatus
无法自动生成。2.2.0
之前,只有当表单域为 Form.Item 的子元素时,才会自动生成help
required
validateStatus
,嵌套情况需要自行设置。
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
label | label 标签的文本 | string|ReactNode | |
labelCol | label 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12} 或 sm: {span: 3, offset: 12} | object | |
wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | object | |
help | 提示信息,如不设置,则会根据校验规则自动生成 | string|ReactNode | |
extra | 额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 | string|ReactNode | |
required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | false |
validateStatus | 校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating' | string | |
hasFeedback | 配合 validateStatus 属性使用,展示校验状态图标,建议只配合 Input 组件使用 | boolean | false |
colon | 配合 label 属性使用,表示是否显示 label 后面的冒号 | boolean | true |
校验规则#
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
message | 校验文案 | string | - |
type | 内建校验类型,可选项 | string | 'string' |
required | 是否必选 | boolean | false |
whitespace | 必选时,空格是否会被视为错误 | boolean | false |
len | 字段长度 | number | - |
min | 最小长度 | number | - |
max | 最大长度 | number | - |
enum | 枚举类型 | string | - |
pattern | 正则表达式校验 | RegExp | - |
transform | 校验前转换字段值 | function(value) => transformedValue:any | - |
validator | 自定义校验(注意,callback 必须被调用) | function(rule, value, callback) | - |
更多高级用法可研究 async-validator。
在 TypeScript 中使用#
import { Form } from 'antd';
import { FormComponentProps } from 'antd/lib/form/Form';
interface UserFormProps extends FormComponentProps {
age: number;
name: string;
}
class UserForm extends React.Component<UserFormProps, any> {
}