Form 表单
如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
npm install @alifd/next@latest -S
开发指南
何时使用
表单布局、校验、数据提交操作时用到。 组件的设计思想可以看这篇文章 https://zhuanlan.zhihu.com/p/56280821
注意事项
组件不要使用关键字
nodeName
作为 name、idForm 默认使用
size=medium
, 并且会控制 FormItem 内所有组件的size。 如果想修改组件的size<FormItem size="small" >
在垂直表单中如果文字(一般
<p>
标签)或者组件向上偏离,可以通过className="next-form-text-align"
辅助调整必须是被
<FormItem>
直接包裹的组件才能展示校验错误提示。如果界面不展示错误信息,请检查是否有多个层级。 比如<FormItem><div><Input/></div></FormItem>
是无法展示校验信息的。
API
Form
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
inline | 内联表单 | Boolean | - |
size | 单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。可选值:'large'(大)'medium'(中)'small'(小) | Enum | 'medium' |
labelAlign | 标签的位置可选值:'top'(上)'left'(左)'inset'(内) | Enum | 'left' |
labelTextAlign | 标签的左右对齐方式可选值:'left'(左)'right'(右) | Enum | - |
field | 经 new Field(this) 初始化后,直接传给 Form 即可 用到表单校验则不可忽略此项 | any | - |
saveField | 保存 Form 自动生成的 field 对象签名:Function() => void | Function | func.noop |
labelCol | 控制第一级 Item 的 labelCol | Object | - |
wrapperCol | 控制第一级 Item 的 wrapperCol | Object | - |
onSubmit | form内有 htmlType="submit" 的元素的时候会触发签名:Function() => void | Function | function preventDefault(e) { e.preventDefault(); } |
children | 子元素 | any | - |
value | 表单数值 | Object | - |
onChange | 表单变化回调签名:Function(values: Object, item: Object) => void参数:values: {Object} 表单数据item: {Object} 详细item.name: {String} 变化的组件名item.value: {String} 变化的数据item.field: {Object} field 实例 | Function | func.noop |
component | 设置标签类型 | String/Function | 'form' |
Form.Item
手动传递了 wrapCol labelCol 会使用 Grid 辅助布局; labelAlign='top' 会强制禁用 Grid
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
label | label 标签的文本 | ReactNode | - |
size | 单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。可选值:'large', 'small', 'medium' | Enum | - |
labelCol | label 标签布局,通 <Col> 组件,设置 span offset 值,如 {span: 8, offset: 16},该项仅在垂直表单有效 | Object | - |
wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | Object | - |
help | 自定义提示信息,如不设置,则会根据校验规则自动生成. | ReactNode | - |
extra | 额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 位于错误信息后面 | ReactNode | - |
validateState | 校验状态,如不设置,则会根据校验规则自动生成可选值:'error'(失败)'success'(成功)'loading'(校验中) | Enum | - |
hasFeedback | 配合 validateState 属性使用,是否展示 success/loading 的校验状态图标, 目前只有Input支持 | Boolean | false |
children | node 或者 function(values) | ReactNode/Function | - |
labelAlign | 标签的位置可选值:'top'(上)'left'(左)'inset'(内) | Enum | - |
labelTextAlign | 标签的左右对齐方式可选值:'left'(左)'right'(右) | Enum | - |
required | 表单校验 不能为空 | Boolean | - |
asterisk | required 的星号是否显示 | Boolean | - |
requiredMessage | required 自定义错误信息 | String | - |
requiredTrigger | required 自定义触发方式 | String/Array | - |
min | 表单校验 最小值 | Number | - |
max | 表单校验 最大值 | Number | - |
minmaxMessage | min/max 自定义错误信息 | String | - |
minmaxTrigger | min/max 自定义触发方式 | String/Array | - |
minLength | 表单校验 字符串最小长度 / 数组最小个数 | Number | - |
maxLength | 表单校验 字符串最大长度 / 数组最大个数 | Number | - |
minmaxLengthMessage | minLength/maxLength 自定义错误信息 | String | - |
minmaxLengthTrigger | minLength/maxLength 自定义触发方式 | String/Array | - |
length | 表单校验 字符串精确长度 / 数组精确个数 | Number | - |
lengthMessage | length 自定义错误信息 | String | - |
lengthTrigger | length 自定义触发方式 | String/Array | - |
pattern | 正则校验 | any | - |
patternMessage | pattern 自定义错误信息 | String | - |
patternTrigger | pattern 自定义触发方式 | String/Array | - |
format | 表单校验 四种常用的 pattern可选值:'number', 'email', 'url', 'tel' | Enum | - |
formatMessage | format 自定义错误信息 | String | - |
formatTrigger | format 自定义触发方式 | String/Array | - |
validator | 表单校验 自定义校验函数签名:Function() => void | Function | - |
validatorTrigger | validator 自定义触发方式 | String/Array | - |
autoValidate | 是否修改数据时自动触发校验 | Boolean | - |
Form.Submit
继承 Button API
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
onClick | 点击提交后触发签名:Function(value: Object, errors: Object, field: class) => void参数:value: {Object} 数据errors: {Object} 错误数据field: {class} 实例 | Function | func.noop |
validate | 是否校验/需要校验的 name 数组 | Boolean/Array | - |
field | 自定义 field (在 Form 内不需要设置) | Object | - |
Form.Reset
继承 Button API
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
names | 自定义重置的字段 | Array | - |
onClick | 点击提交后触发签名:Function() => void | Function | func.noop |
toDefault | 返回默认值 | Boolean | - |
field | 自定义 field (在 Form 内不需要设置) | Object | - |
Form.Error
自定义错误展示
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
name | 表单名 | String/Array | - |
field | 自定义 field (在 Form 内不需要设置) | Object | - |
children | 自定义错误渲染, 可以是 node 或者 function(errors, state) | ReactNode/Function | - |
关于校验
建议一个FormItem放一个组件, 方便错误提示跟随组件展示
组件必须是FormItem的第一层子元素
详细校验请查看
Field
组件文档的 rules
复杂表单场景
如果您的表单场景非常复杂,比如动态渲染,大量字段,复杂数据结构,复杂联动校验,可以考虑使用 uform,uform已经封装了所有fusion组件,保证您开箱即用
代码示例
拉伸浏览器的时候label宽度不变。
如果组件比较靠上,可以用 className="next-form-text-align"
做调整
查看源码在线预览
import { Form, Input, Checkbox } from '@alifd/next';
const FormItem = Form.Item;
const formItemLayout = {
labelCol: {
fixedSpan: 10
},
wrapperCol: {
span: 14
}
};
class Demo extends React.Component {
handleSubmit = (values) => {
console.log('Get form value:', values);
};
render() {
return (
<Form style={{width: '60%'}} {...formItemLayout} >
<FormItem label="baseUsername:">
<p>Fixed Name</p>
</FormItem>
<FormItem label="password:">
<Input htmlType="password" name="basePass" placeholder="Please Enter Password"/>
</FormItem>
<FormItem label="Note:" help="something">
<Input.TextArea placeholder="something" name="baseRemark" />
</FormItem>
<FormItem label="Agreement:">
<Checkbox name="baseAgreement" defaultChecked>Agree</Checkbox>
</FormItem>
<FormItem label=" ">
<Form.Submit onClick={this.handleSubmit}>Confirm</Form.Submit>
</FormItem>
</Form>
);
}
}
ReactDOM.render(<Demo />, mountNode);
查看源码在线预览
import { Form, Input } from '@alifd/next';
const FormItem = Form.Item;
function handleSubmit(v) {
console.log(v);
}
ReactDOM.render(<div>
<Form inline>
<FormItem label="Username:">
<FormItem >
<Input name="first" style={{width: 80}} placeholder="first"/>
</FormItem>
<FormItem >
<Input name="second" style={{width: 80}} placeholder="second"/>
</FormItem>
</FormItem>
<FormItem label="Password:" required hasFeedback={false}>
<Input htmlType="password" name="inlinePass" placeholder="Please enter your password!"/>
</FormItem>
<FormItem label=" ">
<Form.Submit onClick={handleSubmit}>Submit</Form.Submit>
</FormItem>
</Form>
</div>, mountNode);
验证码获取
查看源码在线预览
import { Form, Input } from '@alifd/next';
const FormItem = Form.Item;
const formItemLayout = {
labelCol: { fixedSpan: 3 },
wrapperCol: { span: 20 }
};
class Demo extends React.Component {
state = {
code: '',
second: 60
}
handleSubmit = (values, errors) => {
if (errors) {
return;
}
console.log('Get form value:', values);
};
sendCode = (values, errors) => {
if (errors) {
return;
}
this.setState({
code: Math.floor(Math.random() * (999999 - 99999 + 1) + 99999)
});
setInterval(() => {
this.setState({
second: --this.state.second
});
}, 1000);
}
render() {
const { code } = this.state;
return (
<Form style={{ width: 400 }} {...formItemLayout} labelTextAlign="left" size="large" labelAlign="inset" >
<FormItem label="name" required asterisk={false}>
<Input name="username" trim defaultValue="frank" />
</FormItem>
<FormItem label="phone" format="tel" required asterisk={false}>
<Input name="phone" trim innerAfter={
<Form.Submit
text
type="primary"
disabled={!!code}
validate={['phone']}
onClick={this.sendCode}
style={{ marginRight: 10 }}
>
{code ? `retry after ${this.state.second}s` : 'send code'}
</Form.Submit>
} />
</FormItem>
{
this.state.code ? <FormItem label="code" required asterisk={false}>
<Input name="code" trim defaultValue={this.state.code} />
</FormItem> : null
}
<FormItem label=" ">
<Form.Submit style={{ width: '100%' }} type="primary" validate onClick={this.handleSubmit}>Submit</Form.Submit>
</FormItem>
</Form>
);
}
}
ReactDOM.render(<Demo />, mountNode);
size
会强制设置 FormItem
下的所有组件的size
labelAlign
label方位
labelTextAlign
文字左右对齐方式
查看源码在线预览
import { Form, Input, Select } from '@alifd/next';
const FormItem = Form.Item;
const Option = Select.Option;
const formItemLayout = {
labelCol: {span: 4},
wrapperCol: {span: 20}
};
class Demo extends React.Component {
state = {
size: 'medium'
}
handleChange = (v) => {
this.setState({
size: v
});
}
render() {
return (
<div>
<Form {...formItemLayout} size={this.state.size} style={{maxWidth: '500px'}}>
<FormItem label="Size:">
<Select value={this.state.size} onChange={this.handleChange} style={{width: '100%'}}>
<Option value="small">small</Option>
<Option value="medium">medium</Option>
<Option value="large">large</Option>
</Select>
</FormItem>
<FormItem label="Account:">
<Input placeholder="Please enter your user name" id="userName" name="userName"/>
</FormItem>
<FormItem required label="Password:">
<Input htmlType="password" placeholder="Please enter your password" id="password" name="password"/>
</FormItem>
<FormItem label="Password:" validateState="error">
<Input htmlType="password" placeholder="Check your password" id="rePass" name="rePass"/>
</FormItem>
</Form>
<br/><br/>
<Form size={this.state.size} inline>
<FormItem label="Size:">
<Select style={{width: '100%'}} value={this.state.size} onChange={this.handleChange}>
<Option value="small">small</Option>
<Option value="medium">medium</Option>
<Option value="large">large</Option>
</Select>
</FormItem>
<FormItem label="Account:">
<Input placeholder="Please enter your user name" id="userName2" name="userName2"/>
</FormItem>
<FormItem label="Password:">
<Input htmlType="password" placeholder="Please enter your password" id="password2" name="password2"/>
</FormItem>
<FormItem label="Password:" validateState="error">
<Input htmlType="password" placeholder="Check your password" id="rePass2" name="rePass2"/>
</FormItem>
</Form>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.demo-ctl {
background-color: #f1f1f1;
padding: 10.0px;
color: #0a7ac3;
border-left: 4.0px solid #0d599a;
}
size
会强制设置 FormItem
下的所有组件的size
labelAlign
label方位
labelTextAlign
文字左右对齐方式
查看源码在线预览
import { Form, Input, Select } from '@alifd/next';
const FormItem = Form.Item;
const Option = Select.Option;
class Demo extends React.Component {
render() {
return (
<div>
<Form labelAlign="top" >
<FormItem label="Account:">
<Input placeholder="Please enter your user name" id="userName" name="userName"/>
</FormItem>
<FormItem required label="Password:">
<Input htmlType="password" placeholder="Please enter your password" id="password" name="password"/>
</FormItem>
<FormItem label="Password:" validateState="error">
<Input htmlType="password" placeholder="Please enter your password" id="rePass" name="rePass"/>
</FormItem>
<FormItem label="Size:">
<Select style={{width: '100%'}} name="size">
<Option value="small">small</Option>
<Option value="medium">medium</Option>
<Option value="large">large</Option>
</Select>
</FormItem>
</Form>
<Form inline labelAlign="top">
<FormItem label="Account:">
<Input placeholder="Please enter your user name" id="userName2" name="userName2"/>
</FormItem>
<FormItem label="Password:">
<Input htmlType="password" placeholder="Please enter your password" id="password2" name="password2"/>
</FormItem>
<FormItem label="Password:" validateState="error">
<Input htmlType="password" placeholder="Please enter your password" id="rePass2" name="rePass2"/>
</FormItem>
<FormItem label="Size:">
<Select style={{width: '100%'}} name="size2">
<Option value="small">small</Option>
<Option value="medium">medium</Option>
<Option value="large">large</Option>
</Select>
</FormItem>
</Form>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.demo-ctl {
background-color: #f1f1f1;
padding: 10.0px;
color: #0a7ac3;
border-left: 4.0px solid #0d599a;
}
通过设置labelAlign="inset"
(只适用于Input、Select组件,其他组件不适用)
查看源码在线预览
import { Form, Input, Select } from '@alifd/next';
const FormItem = Form.Item;
const Option = Select.Option;
const formItemLayout = {
labelCol: {fixedSpan: 4},
wrapperCol: {span: 20}
};
ReactDOM.render(<div>
<Form {...formItemLayout} labelAlign="inset" style={{maxWidth: '500px'}}>
<FormItem label="User Name:">
<Input placeholder="Please enter your name" id="insetUserName" name="insetUserName"/>
</FormItem>
<FormItem label="Password:" validateState="error">
<Input htmlType="password" placeholder="Please enter your password" id="insetPassword" name="insetPassword"/>
</FormItem>
<FormItem label="Size:">
<Select style={{width: '100%'}}>
<Option value="small">small</Option>
<Option value="medium">medium</Option>
<Option value="large">large</Option>
</Select>
</FormItem>
</Form>
<br/><br/>
<Form labelAlign="inset" inline>
<FormItem label="User Name:">
<Input placeholder="Enter your name" id="insetUserName2" name="insetUserName2"/>
</FormItem>
<FormItem label="Password:" validateState="error" help="Password is required!">
<Input htmlType="password" placeholder="Enter your password" id="insetPassword2" name="insetPassword2"/>
</FormItem>
<FormItem label="Size:">
<Select style={{width: 150}}>
<Option value="small">small</Option>
<Option value="medium">medium</Option>
<Option value="large">large</Option>
</Select>
</FormItem>
</Form>
</div>, mountNode);
FormItem 嵌套
查看源码在线预览
import { Form, Input, Grid } from '@alifd/next';
const FormItem = Form.Item;
const {Row, Col} = Grid;
const formItemLayout = {
labelCol: {span: 4},
wrapperCol: {span: 14}
};
const insetLayout = {
labelCol: {fixedSpan: 3}
};
ReactDOM.render(<Form {...formItemLayout}>
<FormItem id="control-input" label="Input Something:">
<Row gutter="4">
<Col>
<FormItem label="Nest" labelAlign="inset" {...insetLayout} required requiredTrigger="onBlur" asterisk={false}>
<Input placeholder="Please enter..." name="firstname"/>
</FormItem>
</Col>
<Col>
<FormItem label="Nest" labelAlign="inset" {...insetLayout} required asterisk={false}>
<Input placeholder="need onChange" name="secondname" />
</FormItem>
</Col>
</Row>
</FormItem>
<FormItem label="Bank Account:" >
<Row gutter="4">
<Col>
<FormItem required requiredTrigger="onBlur">
<Input name="A"/>
</FormItem>
</Col>
<Col>
<FormItem required requiredTrigger="onBlur">
<Input name="B"/>
</FormItem>
</Col>
<Col>
<FormItem required requiredTrigger="onBlur">
<Input name="C"/>
</FormItem>
</Col>
<Col>
<FormItem required requiredTrigger="onBlur">
<Input name="D"/>
</FormItem>
</Col>
</Row>
</FormItem>
<FormItem label=" ">
<Form.Submit onClick={(v) => console.log(v)}>Submit</Form.Submit>
</FormItem>
</Form>, mountNode);
标签位置:上、左
配合 Row
Col
控制表单内元素布局 (注意:FormItem非Form直接子元素需要不能直接直接在Form上设置布局)
查看源码在线预览
import { Form, Input, Switch, Grid, Button, Icon, Balloon } from '@alifd/next';
const FormItem = Form.Item;
const {Row, Col} = Grid;
const style = {
padding: '20px',
background: '#F7F8FA',
margin: '20px'
};
const formItemLayout = {
labelCol: {fixedSpan: 4}
};
const label = (<span>
name:<Balloon type="primary" trigger={<Icon type="prompt" size="small" />} closable={false}>blablablablablablablabla</Balloon>
</span>);
class Demo extends React.Component {
state = {
labelAlign: 'top'
}
handleChange = (v) => {
this.setState({
labelAlign: v ? 'left' : 'top'
});
}
render() {
const labelAlign = this.state.labelAlign;
return (
<div>
<h3>Label Position</h3>
<Switch checkedChildren="left" unCheckedChildren="top" checked={this.state.labelAlign === 'left'} onChange={this.handleChange} />
<Form style={style}>
<Row gutter="4">
<Col>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label={label}
required
>
<Input placeholder="Enter a search name:"/>
</FormItem>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Long search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
</Col>
<Col>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Long search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
</Col>
<Col>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Long search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
</Col>
</Row>
<Row>
<Col style={{ textAlign: 'right' }}>
<Button type="primary" style={{ marginRight: '5px' }}>Search</Button>
<Button >Clear All</Button>
</Col>
</Row>
</Form>
<Form style={style}>
<Row gutter="4">
<Col>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label={label}
required
>
<Input placeholder="Enter a search name:"/>
</FormItem>
</Col>
<Col>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Long search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
</Col>
<Col>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
</Col>
<Col>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
</Col>
<Col>
<FormItem {...formItemLayout} labelAlign={labelAlign}
label="Search name:"
>
<Input placeholder="Enter a search name:"/>
</FormItem>
</Col>
</Row>
<Row>
<Col style={{ textAlign: 'right' }}>
<Button type="primary" style={{ marginRight: '5px' }}>Search</Button>
<Button >Clear All</Button>
</Col>
</Row>
</Form>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
需要Form里面有 htmlType="submit" 的元素
查看源码在线预览
import { Form, Input } from '@alifd/next';
const FormItem = Form.Item;
class Demo extends React.Component {
onSubmit(e) {
e.preventDefault(); // form will auto submit if remove this line
console.log('onsubmit');
}
render() {
return (
<Form onSubmit={this.onSubmit.bind(this)}>
<FormItem >
<Input placeholder="Enter Key can also trigger ‘onSubmit’"/>
</FormItem>
<Form.Submit htmlType="submit">submit</Form.Submit>
</Form>
);
}
}
ReactDOM.render(<Demo />, mountNode);
可以通过配置 labelCol
wrapperCol
的 Grid.Col
响应式属性实现响应式
查看源码在线预览
import { Form, Input, Select } from '@alifd/next';
const FormItem = Form.Item;
const formItemLayout = {
labelCol: {xxs: 4, l: 4},
wrapperCol: {xxs: 20, l: 16}
};
ReactDOM.render(<Form {...formItemLayout} >
<FormItem label="userName:">
<Input />
</FormItem>
<FormItem label="password:">
<Input htmlType="password" name="resPass" placeholder="Please Enter Password"/>
</FormItem>
<FormItem label="Country:">
<Select placeholder="Please select a country" style={{width: '100%'}}>
<option value="china">China</option>
<option value="use">United States</option>
<option value="japan">Japan</option>
<option value="korean">South Korea</option>
<option value="Thailand">Thailand</option>
</Select>
</FormItem>
<FormItem label="Note:" help="something">
<Input.TextArea placeholder="something" name="resReremark" />
</FormItem>
<FormItem label=" ">
<Form.Submit>Submit</Form.Submit>
</FormItem>
</Form>, mountNode);
为 <FormItem>
定义 state
属性控制三种校验状态。
如果是 <Input>
组件, 可在<FormItem>
上面添加 hasFeedback
控制图标的展示
注意: 反馈图标只对 <Input />
有效。
查看源码在线预览
import { Form, Input, DatePicker, TimePicker, NumberPicker, Select } from '@alifd/next';
const FormItem = Form.Item;
const formItemLayout = {
labelCol: {
span: 6
},
wrapperCol: {
span: 14
}
};
ReactDOM.render(
<Form {...formItemLayout}>
<FormItem label="Input Error:" validateState="error" help="Please enter a numeric and alphabetic string">
<Input defaultValue="Invalid choice"/>
</FormItem>
<FormItem label="Loading:" hasFeedback validateState="loading" help="Information Checking...">
<Input defaultValue="Checking"/>
</FormItem>
<FormItem label="Success:" hasFeedback validateState="success">
<Input defaultValue="Successful verification"/>
</FormItem>
<FormItem label="Datepicker:" validateState="error" help="Please select the correct date">
<DatePicker />
</FormItem>
<FormItem label="Timepicker:" validateState="error" help="Please select the correct time">
<TimePicker />
</FormItem>
<FormItem label="Select:" validateState="error" help="Please select a country">
<Select placeholder="Please select a country">
<option value="china">China</option>
<option value="use">United States</option>
<option value="japan">Japan</option>
<option value="korean">South Korea</option>
<option value="Thailand">Thailand</option>
</Select>
</FormItem>
<FormItem label="NumberPicker:" validateState="error">
<NumberPicker defaultValue={0} />
</FormItem>
</Form>
, mountNode);
基本的表单校验例子。
查看源码在线预览
import { Form, Input, Radio } from '@alifd/next';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const formItemLayout = {
labelCol: {
span: 6
},
wrapperCol: {
span: 14
}
};
class BasicDemo extends React.Component {
userExists(rule, value) {
return new Promise((resolve, reject) => {
if (!value) {
resolve();
} else {
setTimeout(() => {
if (value === 'frank') {
reject([new Error('Sorry, this username is already exist.')]);
} else {
resolve();
}
}, 500);
}
});
}
render() {
return (
<Form {...formItemLayout} >
<FormItem
label="Account:"
hasFeedback
validator={this.userExists.bind(this)}
help=""
>
<Input placeholder="Input frank" name="valUsername" />
<Form.Error name="valUsername" >{
(errors, state) => {
if (state === 'loading') {
return 'loading...';
} else {
return errors;
}
}
}</Form.Error>
</FormItem>
<FormItem
label="Email:"
hasFeedback
required
requiredTrigger="onBlur"
format="email"
>
<Input placeholder="Both trigget onBlur and onChange" name="valEmail" />
</FormItem>
<FormItem
label="Password:"
hasFeedback
required
requiredMessage="Please enter password"
>
<Input htmlType="password" name="valPasswd" />
</FormItem>
<FormItem
label="Gender:"
hasFeedback
required
requiredMessage="Please select your gender"
>
<RadioGroup name="valSex" >
<Radio value="male">Male</Radio>
<Radio value="female">Female</Radio>
</RadioGroup>
</FormItem>
<FormItem
label="Remarks:"
required
requiredMessage="Really do not intend to write anything?"
>
<Input.TextArea maxLength={20} hasLimitHint placeholder="Everything is ok!" name="valTextarea" />
</FormItem>
<FormItem wrapperCol={{ offset: 6 }} >
<Form.Submit validate type="primary" onClick={(v, e) => console.log(v, e)} style={{marginRight: 10}}>Submit</Form.Submit>
<Form.Reset >Reset</Form.Reset>
</FormItem>
</Form>
);
}
}
ReactDOM.render(<BasicDemo />, mountNode);
基本的表单校验例子。
查看源码在线预览
import { Form, Input, Field } from '@alifd/next';
const FormItem = Form.Item;
const formItemLayout = {
labelCol: {
span: 6
},
wrapperCol: {
span: 14
}
};
class BasicDemo extends React.Component {
field = new Field(this);
render() {
const {
field,
field: {
init,
}
} = this;
return (
<Form {...formItemLayout} field={field} >
<Form.Item label="test" key="test2">
<Input {...init('input', {
rules: [{
validator(_rule, value, callback) {
callback(<span>This is a <em>CUSTOM</em> error</span>);
}
}]
})} />
</Form.Item>
<FormItem wrapperCol={{ offset: 6 }} >
<Form.Submit validate type="primary">Submit</Form.Submit>
<Form.Reset >Reset</Form.Reset>
</FormItem>
</Form>
);
}
}
ReactDOM.render(<BasicDemo />, mountNode);
配合 Field
可以实现较复杂功能
查看源码在线预览
import { Form, Input, Radio, Field, Button } from '@alifd/next';
const FormItem = Form.Item;
const RadioGroup = Radio.Group;
const formItemLayout = {
labelCol: {
span: 6
},
wrapperCol: {
span: 14
}
};
class BasicDemo extends React.Component {
field = new Field(this);
userExists(rule, value) {
return new Promise((resolve, reject) => {
if (!value) {
resolve();
} else {
setTimeout(() => {
if (value === 'frank') {
reject([new Error('Sorry, this username is already occupied.')]);
} else {
resolve();
}
}, 500);
}
});
}
checkPass(rule, value, callback) {
const { validate } = this.field;
if (value) {
validate(['rePasswd']);
}
callback();
}
checkPass2(rule, value, callback) {
const { getValue } = this.field;
if (value && value !== getValue('passwd')) {
return callback('Inconsistent password input twice!');
} else {
return callback();
}
}
validate = () => {
this.field.validate(['sex']);
}
render() {
const {getState, getValue, getError} = this.field;
return (
<Form {...formItemLayout} field={this.field}>
<FormItem
label="Username:"
hasFeedback
required
validator={this.userExists.bind(this)}
help={getState('username') === 'loading' ? 'Checking ...' : getError('username')}
>
<Input placeholder="Input frank" name="username" />
<p>Hello {getValue('username')}</p>
</FormItem>
<FormItem
label="Password:"
hasFeedback
required
requiredMessage="Please enter password"
validator={this.checkPass.bind(this)}
>
<Input htmlType="password" name="passwd" />
</FormItem>
<FormItem
label="Check your password:"
hasFeedback
required
requiredMessage="Enter your password again"
validator={this.checkPass2.bind(this)}
>
<Input htmlType="password" placeholder="Enter the same password twice" name="rePasswd" />
</FormItem>
<FormItem
label="Gender:"
hasFeedback
required
requiredMessage="Please select your gender"
>
<RadioGroup name="sex" >
<Radio value="male">Male</Radio>
<Radio value="female">Female</Radio>
</RadioGroup>
</FormItem>
<FormItem wrapperCol={{ offset: 6 }} >
<Button onClick={this.validate}>Validate by Field</Button>
<Form.Submit validate type="primary" onClick={(v, e) => console.log(v, e)} style={{margin: '0 10px'}}>Submit</Form.Submit>
<Form.Reset >Reset</Form.Reset>
</FormItem>
</Form>
);
}
}
ReactDOM.render(<BasicDemo />, mountNode);
展示和表单相关的其他组件。
查看源码在线预览
import { Form, Input, Button, Checkbox, Radio, Select, Range, Balloon, DatePicker, TimePicker, NumberPicker, Field, Switch, Upload, Grid } from '@alifd/next';
const FormItem = Form.Item;
const Option = Select.Option;
const RangePicker = DatePicker.RangePicker;
const {Row, Col} = Grid;
const formItemLayout = {
labelCol: {span: 6},
wrapperCol: {span: 14}
};
class Demo extends React.Component {
field = new Field(this);
handleSubmit(value) {
console.log('Form values:', value);
}
render() {
const init = this.field.init;
return (
<Form {...formItemLayout} field={this.field}>
<FormItem label="I'm the title:">
<p className="next-form-text-align">The quick brown fox jumps over the lazy dog.</p>
<p ><a href="#">Link</a></p>
</FormItem>
<FormItem label="Password:">
<Balloon trigger={<Input htmlType="password" {...init('pass')} />} align="r" closable={false} triggerType="hover">
input password
</Balloon>
</FormItem>
<FormItem label="NumberPicker:">
<NumberPicker min={1} max={10} name="numberPicker" defaultValue={3} />
<span>Something in here</span>
</FormItem>
<FormItem
label="Switch:"
required>
<Switch name="switch" defaultChecked />
</FormItem>
<FormItem label="Range:" required>
<Range defaultValue={30} scales={[0, 100]} marks={[0, 100]} name="range" />
</FormItem>
<FormItem label="Select:" required>
<Select style={{width: 200}} name="select">
<Option value="jack">jack</Option>
<Option value="lucy">lucy</Option>
<Option value="disabled" disabled>disabled</Option>
<Option value="hugohua">hugohua</Option>
</Select>
</FormItem>
<FormItem
label="DatePicker:"
labelCol={{span: 6}}
required>
<Row>
<FormItem style={{marginRight: 10, marginBottom: 0}}><DatePicker name="startDate"/></FormItem>
<FormItem style={{marginBottom: 0}}><DatePicker name="endDate" /></FormItem>
</Row>
</FormItem>
<FormItem
label="RangePicker:"
labelCol={{span: 6}}
required>
<RangePicker name="rangeDate"/>
</FormItem>
<FormItem
label="TimePicker:"
required>
<TimePicker name="time" />
</FormItem>
<FormItem
label="Checkbox:">
<Checkbox.Group name="checkbox">
<Checkbox value="a">option 1 </Checkbox>
<Checkbox value="b">option 2 </Checkbox>
<Checkbox disabled value="c">option 3(disabled)</Checkbox>
</Checkbox.Group>
</FormItem>
<FormItem
label="Radio:">
<Radio.Group name="radio">
<Radio value="apple">apple</Radio>
<Radio value="banana">banana</Radio>
<Radio disabled value="cherry">cherry(disabled)</Radio>
</Radio.Group>
</FormItem>
<FormItem
label="Logo:"
>
<Upload action="/upload.do" listType="text" name="upload" >
<Button type="primary" style={{margin: '0 0 10px'}}>Upload</Button>
</Upload>
</FormItem>
<Row style={{marginTop: 24}}>
<Col offset="6">
<Form.Submit type="primary" onClick={this.handleSubmit.bind(this)}>Submit</Form.Submit>
</Col>
</Row>
</Form>
);
}
}
ReactDOM.render(<Demo />, mountNode);
在redux
中结合 componentWillReceiveProps
setValues
使用, 配合 Field 使用
查看源码在线预览
import { Form, Input, Button, Checkbox, Field } from '@alifd/next';
import { combineReducers, createStore } from 'redux';
import { Provider, connect } from 'react-redux';
const CheckboxGroup = Checkbox.Group;
function formReducer(state = {email: '', username: 'xiachi', fruit: ['apple']}, action) {
switch (action.type) {
case 'save_fields':
return {
...state,
...action.payload
};
default:
return state;
}
}
class FormDemo extends React.Component {
constructor(props) {
super(props);
this.field = new Field(this, {
onChange: (name, value) => {
console.log('onChange', name, value, this.field.getValues());
this.props.dispatch({
type: 'save_fields',
payload: {
[name]: value
}
});
/* Method 2, Updates all values.
this.props.dispatch({
type: 'save_fields',
payload: this.field.getValues()
});
*/
}
});
}
componentWillReceiveProps(nextProps) {
this.field.setValues(nextProps.formData);
}
setEmail() {
this.props.dispatch({
type: 'save_fields',
payload: {
email: 'qq@gmail.com'
}
});
}
setName() {
this.props.dispatch({
type: 'save_fields',
payload: {
username: 'frank'
}
});
}
setGroup() {
this.props.dispatch({
type: 'save_fields',
payload: {
fruit: ['pear']
}
});
}
render() {
return (<Form field={this.field}>
<Form.Item required requiredMessage="required!">
<Input name="email"/>
</Form.Item>
<Form.Item required requiredMessage="required!">
<Input name="username" defaultValue={this.props.formData.username} />
</Form.Item>
<Form.Item required requiredMessage="required!">
<CheckboxGroup name="fruit" dataSource={[{label: 'Apple', value: 'apple'}, {label: 'Pear', value: 'pear'}]} defaultValue={this.props.formData.fruit}/>
</Form.Item>
<p>email: {this.props.email && this.props.email.value}</p>
<Button onClick={this.setEmail.bind(this)}>setEmail</Button>
<Button onClick={this.setName.bind(this)}>setName</Button>
<Button onClick={this.setGroup.bind(this)}>setGroup</Button>
</Form>);
}
}
const ReduxFormDemo = connect((state) => {
return {
formData: state.formReducer
};
})(FormDemo);
const store = createStore(combineReducers({
formReducer
}));
ReactDOM.render(<Provider store={store}>
<div>
<ReduxFormDemo />
</div>
</Provider>, mountNode);
在redux
中结合 componentWillReceiveProps
setErrors
使用, 配合 Field 使用更加方便
如果需要自己控制错误位置,可以让help=""
然后自己放置展示错误的地方
查看源码在线预览
import { Form, Input, Button, Field } from '@alifd/next';
import { combineReducers, createStore } from 'redux';
import { Provider, connect } from 'react-redux';
const initState = {
values: {email: '', username: 'xiachi'},
errors: {}
};
function formReducer(state = initState, action) {
switch (action.type) {
case 'save_fields':
return {
...state,
values: {
...state.values,
...action.values
}
};
case 'set_errors':
return {
...state,
errors: {
...state.errors,
...action.errors
}
};
default:
return state;
}
}
class FormDemo extends React.Component {
constructor(props) {
super(props);
this.field = new Field(this, {
onChange: (name, value) => {
console.log('onChange', name, value, this.field.getError(name));
this.props.dispatch({
type: 'save_fields',
values: {
[name]: value
}
});
this.props.dispatch({
type: 'set_errors',
errors: {
[name]: this.field.getError(name)
}
});
}
});
}
componentWillReceiveProps(nextProps) {
this.field.setValues(nextProps.formData.values);
this.field.setErrors(nextProps.formData.errors);
}
setEmail() {
this.props.dispatch({
type: 'save_fields',
values: {
email: 'qq@gmail.com'
}
});
}
setName() {
this.props.dispatch({
type: 'save_fields',
values: {
username: 'frank'
}
});
}
setError() {
this.props.dispatch({
type: 'set_errors',
errors: {
email: 'Error message from remote'
}
});
}
setErrors() {
this.props.dispatch({
type: 'set_errors',
errors: {
email: 'A-Error message from remote',
username: 'B-Error message from remote'
}
});
}
render() {
return (<Form field={this.field}>
<Form.Item required format="email">
<Input name="email"/>
</Form.Item>
<Form.Item help="" required requiredMessage="required">
<Input name="username" defaultValue={this.props.formData.values.username} />
<p style={{color: 'blue'}}>{this.field.getError('username')}</p>
</Form.Item>
<p>email: {this.props.email && this.props.email.value}</p>
<Button onClick={this.setEmail.bind(this)}>setEmail</Button>
<Button onClick={this.setName.bind(this)}>setName</Button>
<Button onClick={this.setError.bind(this)}>setError</Button>
<Button onClick={this.setErrors.bind(this)}>setErrors</Button>
<Button onClick={() => this.field.reset()}>reset</Button>
</Form>);
}
}
const ReduxFormDemo = connect((state) => {
return {
formData: state.formReducer
};
})(FormDemo);
const store = createStore(combineReducers({
formReducer
}));
ReactDOM.render(<Provider store={store}>
<div>
<ReduxFormDemo />
</div>
</Provider>, mountNode);
对于必填项,在组件中要设置aria-required
属性,并通过视觉设计上的高亮提示用户。
查看源码在线预览
import { Form, Input, Select, Radio, Checkbox, DatePicker, Switch, Upload, Grid, Field } from '@alifd/next';
const RadioGroup = Radio.Group;
const {Row, Col} = Grid;
const FormItem = Form.Item;
const Option = Select.Option;
const formItemLayout = {
labelCol: {
span: 7
},
wrapperCol: {
span: 16
}
};
class Demo extends React.Component {
state = {
size: 'medium'
};
submitHandle = (e) => {
console.log(e);
};
render() {
return (
<div>
<Form {...formItemLayout} size={this.state.size} style={{maxWidth: '800px'}}>
<FormItem required label="username:">
<Input placeholder="Please enter your username" id="a11yUsername" name="a11yUsername" aria-required="true" />
</FormItem>
<FormItem required label="Password:">
<Input htmlType="password" placeholder="Please enter your password" id="a11yPassword" name="a11yPassword" aria-required="true" />
</FormItem>
<FormItem
id="myDateInput-1"
required
label="Accessible Date 1 (YYYY/MM/DD):"
requiredMessage="Please select your date"
>
<DatePicker name="a11yDate" format="YYYY/MM/DD" inputProps={{"aria-required": "true", "id": "myDateInput-1"}}/>
</FormItem>
<FormItem
required
label="Accessible Date 2 (YYYY/MM/DD):"
requiredMessage="Please select your date"
>
<DatePicker name="a11yOtherDate" format="YYYY/MM/DD" dateInputAriaLabel="Date input format YYYY/MM/DD" inputProps={{"aria-required": "true", "aria-label": "Accessible Date 2"}}/>
</FormItem>
<FormItem label="Switch:">
<Switch name="a11ySwitch" aria-label="Accessible Switch" defaultChecked/>
</FormItem>
<FormItem
required
label="gender:"
requiredMessage="Please select your gender"
>
<RadioGroup name="a11ySex">
<Radio value="male" aria-required="true">Male</Radio>
<Radio value="female" aria-required="true">Female</Radio>
</RadioGroup>
</FormItem>
<FormItem label="Language:">
<Checkbox.Group name="a11yLangs" aria-label="Please select a programming language">
<Checkbox value="python">python</Checkbox>
<Checkbox value="java">java</Checkbox>
<Checkbox value="angular">angular</Checkbox>
<Checkbox value="c">c</Checkbox>
<Checkbox value="other">other</Checkbox>
</Checkbox.Group>
</FormItem>
<FormItem label="upload:">
<Upload.Card
listType="card"
action="https://www.easy-mock.com/mock/5b713974309d0d7d107a74a3/alifd/upload"
accept="image/png, image/jpg, image/jpeg, image/gif, image/bmp"
defaultValue={[]}
limit={2}
name="a11yUpload"
/>
</FormItem>
<FormItem label="Note:">
<Input.TextArea placeholder="description" name="a11yRemark"/>
</FormItem>
<FormItem wrapperCol={{offset: 5}}>
<Form.Submit validate type="primary" onClick={this.submitHandle} style={{marginRight: 7}}>Submit</Form.Submit>
<Form.Reset style={{marginLeft: 130}}>Reset</Form.Reset>
</FormItem>
</Form>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);