表单组件
参数名 | 说明 | 必填 | 类型 | 默认值 | 备注 |
---|---|---|---|---|---|
initialValues | 表单初始值 | N | object | {} | - |
onSubmit | submit函数 | Y | function | - | - |
onChange | 表单变化回调 | N | function | - | function(values: object, item: object) => void 参数: values: {object} 表单数据 item: {object} 详细 item.name: {string} 变化的组件名 item.value: {string} 变化的数据 |
rules | 校验规则 | N | object | {} | - |
effects | 联动规则 | N | array | [] | - |
layout | 表单布局 | N | object | - | |
renderField | 自定义 Field 布局 | N | function | - | function({label, component, error}) => dom 参数: label: {string/element} Field 的 label component: {string/function} 待渲染的控件 error: {string/element} Field 错误提示信息 |
其他属性比如 style
、className
等均会传递到 form
标签上。
layout
是个对象,包含 4 个属性:
{
labelAlign: 'left', // label 的位置,'left'、'top',默认 'left'
labelTextAlign: 'right', // label 文字对齐方式,'left'、'right',默认 'right'
labelCol: 1, // label 占的栅格宽度,共 12 等分,默认 2
wrapperCol: 3, // 控件占的栅格宽度,共 12 等分,默认 6
}
rules
是一个 Object,key
是 <Field>
的 name
属性值,value
是个数组,数组里面的每一项是一个校验规则,参考 async-validator。
<Form
onSubmit={this.onSubmit}
style={{color: '#ee7893'}}
rules={{
username: [{
required: true,
min: 5,
message: '姓名至少5个字符'
}],
age: [{
required: true,
message: '年龄必填'
}]
}}
>
<Field label="姓名:" name="username" component="input" type="text" />
<Field label="年龄:" name="age" component='input' type="number" />
</Form>
effects
是个数组,写法如下:
<Form
onSubmit={this.onSubmit}
effects={[
{
field: 'username',
handler: formCore => {
if (formCore.getFieldValue('username') === 'ice') {
formCore.setFieldValue('age', 2)
}
}
}
]}
>
<div>Hello Form</div>
<Field label="姓名:" name="username" component="input" type="text" />
<Field label="年龄:" name="age" component='input' type="number" />
<button type="submit">Submit</button>
</Form>
监听该 field
的 onChange
事件,然后设置其他表单项的数据,从而达到联动效果。handler
的参数是 formCore
对象,该对象暴露一些 api 可以设置 value、error、show/hide 等。
参数名 | 说明 | 必填 | 类型 | 默认值 | 备注 |
---|---|---|---|---|---|
label | 表单项的 label | N | string/element | - | - |
name | 表单项的 name | Y | string | - | - |
component | 表单类型,原生 html 标签或者三方组件 | N | string/function | - | 'input' 'textarea' Input Radio |
value | 表单项的值 | N | - | '' | - |
rules | 校验规则 | N | object or array | - | - |
effects | 联动规则 | N | object | - | - |
visible | 显示隐藏当前 Field | N | boolean | true | true/false |
setValueFormatter | 格式化控件渲染值 | N | function | function(savedValue) => renderValue | |
getValueFormatter | 格式化控件提交值 | N | function | function(renderValue) => savedValue | |
layout | 设置当前 Field 的布局 | N | object | 同 Form layout | 当前 Field 的 layout 会覆盖 Form 的 layout |
tips | 提示信息 | N | string | ||
valueName | 控件值的名称,比如,radio 的 valueName 为 'checked',value 为 true/false | N | string | 比如 Fusion 的 Switch 组件 | |
errorRender | 自定义 error 渲染 | N | function(error) {} | ||
onChange | 自定义 onChange 函数 | N | function() {} | 默认情况下已处理表单的 onChange(eventOrValue) 事件,如果接入的三方控件 onChange 的第一个参数不是 event 或者 value,可以主动设置对应的值。比如,接入控件的 onChange(xxx, value) 第二个参数才是 value,则可以手动设置 formCore.setValue(fieldname, value) |
style
、className
属性会传递到 Field 最外层 dom 上,其他属性会传递到 component
上,如果没有 component
但有 children
,则属性传递到 children
上。
Field
的 rules
和 effects
不需要 name
作为 key 了,写法如下:
<Form onSubmit={this.onSubmit}>
<Field label="姓名:" name="username" component="input" type="text" />
<Field label="昵称:" name="nickname" component="input" type="text" effects={{
handler: formCore => {
if (formCore.getFieldValue('nickname') === 'snow') {
formCore.setFieldProps('age', {
visible: true,
});
} else {
formCore.setFieldProps('age', {
visible: false,
});
}
}
}} />
<Field label="年龄:" name="age" component='input' type="number" rules={[{
required: true,
message: '年龄必填'
}]} />
<button type="submit">Submit</button>
</Form>
FieldArray 表示渲染数组类型的数据,属性同 Field:
<Form
onSubmit={this.onSubmit}
>
<FieldArray label="新增顾客:" name="customers">
<Field name="customer0" component={Input} placeholder="customer name" />
<Field name="customer1" component={Input} placeholder="customer name" />
<Field name="customer2" component={Input} placeholder="customer name" />
</FieldArray>
<Field label="日期:" name="date" component={DatePicker} />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
formCore
APIformCore
会暴露一些 API,使用这些 API 可以获取、设置表单的数据、状态等。
getFieldValue(name)
:获取某一 Field
的值setFieldValue(name, value)
:设置某一 Field
的值getValues()
:获取表单的 valuessetValues(values, runEffects)
:设置表单的 values,runEffects 为 Boolean,表示设置 values 之后是否需要执行表单的 effects,默认 falsegetFieldError(name)
:获取某一 Field
的 error 信息setFieldError(name, errMsg)
:设置某一 Field
的 error 信息getErrors()
:获取所有 Field
的 error 信息setErrors(errors)
:设置某些 Field
的 error 信息getFieldProps(name)
:获取某一 Field
的属性值setFieldProps(name, prop)
:设置某一 Field
的属性值submit()
:提交表单reset(initialValues)
:重置表单值为表单初始化时的默认值,如果表单初始化时没有默认值,则清空表单;如果传了参数 initialValues,则 initialValues 会成为新的表单默认值也可以通过属性的方式获取到一些数据:
formCore.values
:获取表单的所有值formCore.errors
:获取表单校验的错误信息formCore.pristine
:表单当前的 values
是否与 initialValues
相等对于前端,表单开发是一件特别繁琐的事情,尤其在中后台业务中,大家常常会被各种五花八门的表单折磨,又不得不面对现实地去寻找最佳方案,但最终都会发现过度设计的表单组件性能不好,使用简单的表单组件还是需要写大量的业务代码。经过长期的积累以及在社区的调研,我们开发了一个表单组件帮助大家快速地创建一个高性能表单。
如上图所示,整个表单的数据都放在 FormCore 这一层,同时 FormCore 会暴露一些 API,以便获取、设置、处理数据。Form、Field 组件通过 Sub/Pub 模式与 FormCore 通信,FormCore 通知组件何时重新渲染。表单提供了校验、联动以及结合 Fusion、Antd 三方组件库使用等能力。
NoForm 是一个表单操作(比如说校验、提交、联动等)抽象到上层,下层又包装了 Next、Antd 等组件,UI 上的能力较强,也封装了一些常用的布局,但功能能力较弱,用户实现复杂逻辑还是需要写很多代码。
这两个组件有一些共性,都是通过 render props 的方式实现了复杂的状态管理,在性能上也非常地卓越,在社区得到了大量的好评,但在联动上的能力较弱(目前只能更新 value),而且如果要集成 Next 或者 Antd 需要将库的表单组件都封装成 Field,成本较高。
Form
的基本用法
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input, Switch, Select, Checkbox } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const Option = Select.Option;
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
onChange(values, field) {
console.log(values, field);
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
onChange={this.onChange}
initialValues={{
username: 'icer',
age: 3,
intro: '让前端开发简单而友好'
}}
>
<h2>个人资料</h2>
<Field label="姓名:" name="username" component={Input} placeholder="请输入名字" />
<Field label="年龄:" name="age" component={Input} htmlType="number" placeholder="请输入年龄" />
<Field label="简介:" name="intro" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" />
<Field label="开关:" name="open" component={Switch} valueName="checked" />
<Field label="尺寸:" name="size" component={Select} value="medium" placeholder="请选择尺寸">
<Option value="small" key="small">小</Option>
<Option value="medium" key="medium">中</Option>
<Option value="large" key="large">大</Option>
</Field>
<Field label="选项:" name="checkbox" value={['b']} component={Checkbox.Group}>
<Checkbox value="a">选项一</Checkbox>
<Checkbox value="b">选项二</Checkbox>
<Checkbox disabled value="c">选项三(disabled)</Checkbox>
</Field>
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
Form
子组件使用 function
渲染时,可以调用 FormCore
中的 API
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
const initialValues = {
username: 'icer',
age: 3,
intro: '让前端开发简单而友好'
};
return (
<div>
<Form
onSubmit={this.onSubmit}
initialValues={initialValues}
>
{formCore => (
<div>
<h2>个人资料</h2>
<Field label="姓名:" name="username" component={Input} placeholder="请输入名字" />
<Field label="年龄:" name="age" component={Input} htmlType="number" placeholder="请输入年龄" />
<Field label="简介:" name="intro" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" />
<Field label="">
<div>
<Button htmlType="submit" style={{marginRight: '20px'}}>Submit</Button>
<Button onClick={() => formCore.reset()}>Reset</Button>
</div>
</Field>
</div>
)}
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
校验规则可以写在 Form
属性上或者 Field
属性上
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
rules={{
username: [{
required: true,
min: 5,
message: '姓名至少5个字符'
}]
}}
>
<h2>个人资料</h2>
<Field label="姓名:" name="username" component={Input} placeholder="请输入名字" placeholder="请输入名字"/>
<Field label="年龄:" name="age" component={Input} htmlType="number" placeholder="请输入年龄" rules={[{
message: '年龄必填且大于18岁',
required: true,
validator: (rule, value) => value > 18
}]} />
<Field label="简介:" name="intro" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
异步校验,校验结果的 message
直接 callback
即可
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form onSubmit={this.onSubmit}>
<h2>个人资料</h2>
<Field name="name" label="名称:" component={Input} autoComplete="off" placeholder="请输入名字" rules={{
async asyncValidator(rule, value, callback) {
if (!value) {
callback('名称必填');
} else {
await sleep(500);
if (~['john', 'paul', 'george', 'ringo'].indexOf(value)) {
callback('名称已存在');
} else {
callback(undefined)
}
}
}
}} />
<Field label="年龄:" name="age" component={Input} htmlType="number" placeholder="请输入年龄" rules={[{
message: '年龄必填且大于18岁',
required: true,
validator: (rule, value) => value > 18
}]} />
<Field label="简介:" name="intro" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
基础联动
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input, Radio } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
rules={{
username: [{
required: true,
min: 5,
message: '姓名至少5个字符'
}],
age: [{
required: true,
message: '大于18岁',
validator: (rule, value) => value > 18
}]
}}
effects={[
{
field: 'username',
handler: formCore => {
const name = formCore.getFieldValue('username');
if (name === 'Khaleesi') {
formCore.setFieldValue('age', 28)
}
}
},
{
field: 'gender',
handler: formCore => {
const gender = formCore.getFieldValue('gender');
if (gender === 'female') {
formCore.setFieldProps('age', {
visible: false,
});
} else {
formCore.setFieldProps('age', {
visible: true,
});
}
}
}
]}
>
<h2>个人资料</h2>
<Field label="姓名:" name="username" component={Input} placeholder="请输入名字" />
<Field label="性别:" name="gender" component={Radio.Group}>
<Radio value="male">男</Radio>
<Radio value="female">女</Radio>
<Radio value="x">X</Radio>
</Field>
<Field label="年龄:" name="age" component={Input} htmlType="number" placeholder="请输入年龄" />
<Field label="简介:" name="intro" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
省市联动
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Input, Button, Select } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const provinceData = ['Zhejiang', 'Hubei', 'Jiangsu'];
const cityData = {
Zhejiang: ['Hangzhou', 'Ningbo', 'Wenzhou'],
Hubei: ['Wuhan', 'Yichang', 'Jingzhou'],
Jiangsu: ['Nanjing', 'Suzhou', 'Zhenjiang']
};
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
effects={[
{
field: 'province',
handler: formCore => {
const province = formCore.getFieldValue('province')
formCore.setValues({
city: '',
population: 0
})
formCore.setFieldProps('city', {dataSource: cityData[province]})
}
},
{
field: 'city',
handler: formCore => {
const city = formCore.getFieldValue('city')
if (city === 'Suzhou') {
formCore.setFieldProps('population', {disabled: true})
formCore.setFieldValue('population', 0)
} else {
formCore.setFieldProps('population', {disabled: false})
}
}
}
]}
>
<div style={{
marginBottom: 20,
}}>当城市为“苏州”,则 disabled 人口</div>
<Field label="省:" layout={{wrapperCol: 2}} name="province" placeholder="Select Province" dataSource={provinceData} component={Select} />
<Field label="市:" layout={{wrapperCol: 2}} name="city" placeholder="Select City" dataSource={[]} component={Select} />
<Field label="人口:" name="population" placeholder="The population of the city" component={Input} htmlType="number" />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
同时监听两个 Field
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
effects={[
{
field: 'number1',
handler: formCore => {
const value1 = formCore.getFieldValue('number1');
const value2 = formCore.getFieldValue('number2');
if (Number(value1) < Number(value2)) {
formCore.setFieldError('number2', 'number1不能小于number2');
} else {
formCore.setFieldError('number2', undefined);
}
}
},
{
field: 'number2',
handler: formCore => {
const value1 = formCore.getFieldValue('number1');
const value2 = formCore.getFieldValue('number2');
if (Number(value1) < Number(value2)) {
formCore.setFieldError('number2', 'number1不能小于number2');
} else {
formCore.setFieldError('number2', undefined);
}
}
}
]}
>
<Field label="Number1:" name="number1" component={Input} htmlType="number" placeholder="number1 必须大于等于 number2" />
<Field label="Number2:" name="number2" component={Input} htmlType="number" placeholder="number2 必须小于等于 number1" />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
通过 Form 的 renderField 自定义 Field 布局
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
renderField={({label, component, error}) => (
<div style={{marginBottom: '10px'}}>
<div>{label}</div>
<span>{component}</span>
<span style={{color: '#ee7893'}}>{error}</span>
</div>
)}
rules={{
username: [{
required: true,
min: 5,
message: '姓名至少5个字符'
}]
}}
>
<Field label="姓名:" name="username" component={Input} placeholder="请输入名字" />
<Field label="年龄:" name="age" component={Input} htmlType="number" placeholder="请输入年龄" />
<Button htmlType="submit">Submit</Button>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
Format Feild's value
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, DatePicker, Input } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form onSubmit={this.onSubmit}>
<h4>姓名显示、存储均为小写,日期存储格式为时间戳</h4>
<Field name="name" label="姓名:" component={Input} placeholder="请输入姓名" setValueFormatter={
value => value && value.toLowerCase()
} getValueFormatter={
value => value && value.toLowerCase()
} />
<Field label="生日:" name="date" component={DatePicker} getValueFormatter={
value => Date.parse(value)
} />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
const Address = ({ name, label }) => (
<React.Fragment>
<div>
<Field
name={`${name}.street`}
component={Input}
label={`${label} Street:`}
placeholder={`${label} Street`}
/>
</div>
<div>
<Field
name={`${name}.city`}
label={`${label} City:`}
component={Input}
placeholder={`${label} City`}
/>
</div>
<div>
<Field
name={`${name}.postalCode`}
label={`${label} Postal Code:`}
component={Input}
placeholder={`${label} Postal Code`}
/>
</div>
</React.Fragment>
)
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form onSubmit={this.onSubmit}>
<h1>可复用的 Field Group</h1>
<Address name="billing" label="Billing" />
<Address name="shipping" label="Shipping" />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
异步加载数据进行首屏渲染
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const load = async () => {
await sleep(2000);
return {
name: 'erikras',
age: 20,
desc: '我是 erikras'
};
}
class App extends Component {
state = { data: {} }
async componentDidMount() {
this.setState({ loading: true });
const data = await load();
this.setState({ loading: false, data });
}
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
const Age = (<span style={{color: 'green'}}>年龄:</span>);
return (
<div>
<h2>加载表单数据并且初始化(label支持自定义jsx)</h2>
<Form
initialValues={this.state.data}
onSubmit={this.onSubmit}
effects={[{
field: 'name',
handler: formCore => {
const name = formCore.getFieldValue('name');
if (name === 'erikras') {
formCore.setFieldValue('age', 28)
}
}
}]}
>
{this.state.loading && <div className="loading">loading...</div>}
<Field name="name" label={<span style={{color: 'red'}}>名称:</span>} component={Input} placeholder="请输入名字" />
<Field name="age" label={Age} component={Input} placeholder="请输入年龄" />
<Field name="desc" label="简介:" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
Form 结合对话框
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Input, Button, Dialog, Switch } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
handleSubmit = null;
state = {
visible: false
};
onOpen = () => {
this.setState({
visible: true
});
};
onClose = reason => {
this.setState({
visible: false
});
};
onOk = (e) => {
this.handleSubmit(e);
};
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Button onClick={this.onOpen} type="primary">
Open dialog
</Button>
<Dialog
title="表单 Form 对话框"
visible={this.state.visible}
onOk={this.onOk.bind(this)}
onCancel={this.onClose.bind(this, 'cancelClick')}
onClose={this.onClose}
style={{
width: 600,
}}
>
<Form
onSubmit={this.onSubmit}
layout={{
labelCol: 4,
wrapperCol: 8
}}
>
{formCore => {
this.handleSubmit = formCore.submit.bind(formCore);
return (
<div>
<Field name="name" label="名称:" component={Input} placeholder="请输入名字" />
<Field name="age" label="年龄:" component={Input} placeholder="请输入年龄" />
<Field name="desc" label="简介:" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" />
<Field name="open" label="是否打开:" component={Switch} valueName="checked" value={true} />
<Field name="openDesc" label="打开时的描述:" component={Input} placeholder="description when opening"/>
<Field name="closeDesc" label="关闭时的描述:" component={Input} placeholder="description when closing" />
</div>
)
}}
</Form>
</Dialog>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
使用 Antd 组件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input, Select, Switch, Checkbox, Radio } from 'antd';
import 'antd/dist/antd.css';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
>
{formCore => (
<div>
<h2>Antd 组件</h2>
<Field label="姓名:" name="username" component={Input} placeholder="请输入名字" />
<Field label="年龄:" name="age" component={Input} type="number" placeholder="请输入年龄" />
<Field label="简介:" name="intro" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" />
<Field label="性别:" name="gender" rules={[{
required: true,
message: '性别必选'
}]} component={Radio.Group}>
<Radio id="apple" value="male">Male</Radio>
<Radio id="watermelon" value="female">Female</Radio>
<Radio id="orange" value="x">X</Radio>
</Field>
<Field label="前端框架:" name="framework" value={[]} component={Checkbox.Group}>
<Checkbox value="react">React</Checkbox>
<Checkbox value="vue">Vue</Checkbox>
<Checkbox value="angular">Angular</Checkbox>
</Field>
<Field label="加班:" name="switch" component={Switch} valueName='checked' value={true} checkedChildren="on" unCheckedChildren="off" />
<Field label="职级:" name="grade" component={Select}>
<Select.Option value="p6">P6</Select.Option>
<Select.Option value="p7">P7</Select.Option>
<Select.Option value="p8" disabled>P8</Select.Option>
</Field>
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</div>
)}
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
使用原生 html 标签
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
rules={{
username: [{
required: true,
min: 5,
message: '姓名至少5个字符'
}]
}}
>
<h2>个人资料</h2>
<Field label="姓名:" name="username" component="input" placeholder="请输入名字" />
<Field label="年龄:" name="age" component="input" type="number" placeholder="请输入年龄" />
<Field label="简介:" name="intro" component="textarea" placeholder="请简单介绍一下自己的工作经历" />
<Field label="框架:" name="framework" component="select" value="react">
<option value="vue">Vue</option>
<option value="react">React</option>
<option value="angular">Angular</option>
</Field>
<Field label="ICE背景:" name="ice" component="input" valueName='checked' value={false} type="radio" />
<Field label="">
<button type="submit">Submit</button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
state = {
renderField: undefined,
data: {},
rules: {
username: [{
required: true,
min: 5,
message: '姓名至少5个字符'
}]
}
}
async componentDidMount() {
await sleep(1000);
this.setState({
renderField: ({label, component, error}) => (
<div style={{marginBottom: '15px'}}>
<div>{label}</div>
<span>{component}</span>
<span style={{marginLeft: '10px', color: '#ee7893'}}>{error}</span>
</div>
),
data: {
username: 'erikras',
age: 20,
desc: '我是 erikras'
},
rules: {
username: [{
required: true,
min: 10,
message: '姓名至少10个字符'
}]
}
})
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
initialValues={this.state.data}
renderField={this.state.renderField}
rules={this.state.rules}
>
<Field label="姓名:" name="username" component={Input} placeholder="请输入名字" />
<Field label="年龄:" name="age" component={Input} htmlType="number" placeholder="请输入年龄" />
<Field label="简介: " name="desc" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
表单布局
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
const layout = {
labelCol: 2,
wrapperCol: 4,
labelTextAlign: 'left',
labelAlign: 'top'
};
return (
<div>
<Form
layout = {layout}
onSubmit={this.onSubmit}
rules={{
username: [{
required: true,
min: 5,
message: '姓名至少5个字符'
}]
}}
>
<h2>个人资料</h2>
<Field label="姓名:" name="username" component={Input} placeholder="请输入名字" errorRender={
error => <span style={{color: '#ff7000'}}>{error}</span>
} />
<Field label="年龄:" name="age" component={Input} tips="身份证上的年龄" placeholder="请输入年龄" />
<Field label="简介:" name="intro" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历" tips="介绍自己的经历介绍自己的经历介绍自己的经历介绍自己的经历" />
<Field label="" name="submit">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
标签布局,当前支持 labelTextAlign 和 labelAlign
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
class App extends Component {
render() {
return (
<div>
<Form
layout = {{
labelTextAlign: 'left'
}}
>
<h2>标签靠左(labelTextAlign: left)</h2>
<Field label="name: " name="name" component={Input} placeholder="please input name" />
<Field label="age: " name="age" component={Input} placeholder="please input age" />
<Field label="intro: " name="intro" component={Input} placeholder="please input intro" />
</Form>
<Form
layout = {{
labelTextAlign: 'right'
}}
>
<h2>标签靠右(labelTextAlign: right)</h2>
<Field label="name: " name="name" component={Input} placeholder="please input name" />
<Field label="age: " name="age" component={Input} placeholder="please input age" />
<Field label="intro: " name="intro" component={Input} placeholder="please input intro" />
</Form>
<Form
layout = {{
labelAlign: 'top',
labelTextAlign: 'left'
}}
>
<h2>标签在上面(labelAlign: top)</h2>
<Field label="name: " name="name" component={Input} placeholder="please input name" />
<Field label="age: " name="age" component={Input} placeholder="please input age" />
<Field label="intro: " name="intro" component={Input} placeholder="please input intro" />
</Form>
<Form
layout = {{
labelAlign: 'left'
}}
>
<h2>标签在左边(labelAlign: left)</h2>
<Field label="name: " name="name" component={Input} placeholder="please input name" />
<Field label="age: " name="age" component={Input} placeholder="please input age" />
<Field label="intro: " name="intro" component={Input} placeholder="please input intro" />
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
Form
自定义标签,给每个标签增加必填符号
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input, Switch, Select, Checkbox } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const Option = Select.Option;
function renderLabel(name) {
return (
<span>
<span style={{
color: 'red',
}}>*</span>
{name}
</span>
);
}
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
rules={{
username: [{
required: true,
min: 5,
message: '姓名至少5个字符'
}],
age: [{
required: true,
message: '年纪必填'
}],
intro: [{
required: true,
message: '简介必填'
}],
}}
>
<h2>个人资料</h2>
<Field label={renderLabel('姓名:')} name="username" component={Input} placeholder="请输入名字" />
<Field label={renderLabel('年龄:')} name="age" component={Input} htmlType="number" placeholder="请输入年龄" />
<Field label={renderLabel('简介:')} name="intro" component={Input.TextArea} placeholder="请简单介绍一下自己的工作经历"/>
<Field label="开关:" name="open" component={Switch} />
<Field label="尺寸:" name="size" component={Select} placeholder="请选择尺寸">
<Option value="small" key="small">小</Option>
<Option value="medium" key="medium">中</Option>
<Option value="large" key="large">大</Option>
</Field>
<Field label="选项:" name="checkbox" component={Checkbox.Group}>
<Checkbox value="a">选项一</Checkbox>
<Checkbox value="b">选项二</Checkbox>
<Checkbox disabled value="c">选项三(disabled)</Checkbox>
</Field>
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
输入框后面的数字代表每个 Field 渲染的次数。 当在某个 Field 输入内容时,可以看到 form 组件,只有当前的 Field 会重新渲染,而 formBinder 则是所有的 Field 都会重新渲染。form 渲染性能优于 formBinder。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input } from '@alifd/next';
import { FormBinderWrapper, FormBinder } from '@icedesign/form-binder';
const renderCountStyles = {
position: 'absolute',
top: 0,
right: 0,
fontStyle: 'normal',
textAlign: 'center',
height: '26px',
width: '26px',
lineHeight: '26px',
borderRadius: '13px',
border: '1px solid #ddd',
background: '#eee'
};
class RenderCount extends Component {
renders = 0
render() {
return <span style={renderCountStyles}>{++this.renders}</span>
}
}
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
let dataSource = [];
for (let i = 0; i < 150; i++) {
const item = {
label: `Label${i}`,
name: `name${i}`,
placeholder: `placeholder ${i}`
};
dataSource.push(item);
}
class App extends Component {
state = {
value: {
email: '',
name: '',
password: '',
},
isFormBinder: false
};
toggle = () => {
this.setState({
isFormBinder: !this.state.isFormBinder
})
}
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<h3>表单性能测试 form vs formBinder</h3>
<Button onClick={this.toggle} style={{marginBottom: '10px'}}>Toggle Form</Button>
{!this.state.isFormBinder && (
<Form
onSubmit={this.onSubmit}
renderField={({label, component, error}) => (
<div style={{marginBottom: '10px'}}>
<span style={{display: 'inline-block', width: '80px'}}>{label}</span>
<div style={{position: 'relative', display: 'inline-block'}}>
<span>{component}</span>
<RenderCount />
</div>
<RenderCount />
<span style={{color: '#ee7893'}}>{error}</span>
</div>
)}
>
{
dataSource.map(field => <Field key={field.name} name={field.name} placeholder={`new form ${field.placeholder}`} label={field.label} component={Input} /> )
}
<Button htmlType="submit">Submit</Button>
</Form>
)}
{
this.state.isFormBinder && (
<div>
<FormBinderWrapper
value={this.state.value}
ref="form"
>
{
dataSource.map(field => (
<div key={field.name} style={{marginBottom: '10px'}}>
<span style={{display: 'inline-block', width: '80px'}}>{field.label}:</span>
<FormBinder name={field.name} require >
<div style={{position: 'relative', display: 'inline'}}>
<RenderCount />
<Input placeholder={`formBinder ${field.placeholder}`} />
</div>
</FormBinder>
</div>
))
}
</FormBinderWrapper>
</div>
)
}
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input, Switch, Select, Checkbox } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const Option = Select.Option;
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
className="ice-inline-form"
>
<h2>个人资料</h2>
<div className="field-wrapper">
<Field label="姓名:" name="username" component={Input} placeholder="请输入姓名" />
<Field label="年龄:" name="age" component={Input} htmlType="number" placeholder="请输入年龄" />
<Field label="地址:" name="address" component={Input} placeholder="请输入地址" />
<Field label="手机:" name="tel" component={Input} htmlType="number" placeholder="请输入手机号码" />
<Field label="邮件:" name="email" component={Input} placeholder="请输入邮件地址" />
</div>
<Button className="btn" htmlType="submit">Submit</Button>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
.ice-inline-form .field-wrapper {
display: flex;
flex-wrap: wrap;
}
.ice-inline-form .ice-field {
width: 50%;
height: 40px;
}
.ice-inline-form .field-wrapper {
overflow: hidden;
background-color: #ddd;
padding-top: 15px;
padding-bottom: 15px;
}
.ice-inline-form .btn {
margin-top: 20px;
}
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input, Switch, Select, Checkbox } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const Option = Select.Option;
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
layout={{
labelTextAlign: 'left'
}}
className="ice-first-inline-form"
>
<h2>个人资料</h2>
<div style={{display: 'flex'}}>
<Field style={{flexBasis: '35%'}} label="姓名:" name="username" component={Input} placeholder="请输入姓名" />
<Field style={{flexBasis: '30%'}} label="年龄:" name="age" component={Input} htmlType="number" placeholder="请输入年龄" />
</div>
<Field label="地址:" name="address" component={Input} placeholder="请输入地址" />
<Field label="手机:" name="tel" component={Input} htmlType="number" placeholder="请输入手机号码" />
<Field label="邮件:" name="email" component={Input} placeholder="请输入邮件地址" />
<Button style={{marginLeft: '60px', marginTop: '15px'}} className="btn" htmlType="submit">Submit</Button>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
.ice-first-inline-form .ice-field .ice-field-label {
display: inline;
width: auto ;
}
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, FieldArray, Field } from '@ice/form';
import { Button, Input, DatePicker } from '@alifd/next';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
>
<h2>新增顾客名单</h2>
<FieldArray label="新增顾客:" name="customers">
<Field className="field-array-item" name="customer0" component={Input} placeholder="请输入顾客的名字" />
<Field className="field-array-item" name="customer1" component={Input} placeholder="请输入顾客的名字" />
<Field className="field-array-item" name="customer2" component={Input} placeholder="请输入顾客的名字" />
</FieldArray>
<Field label="日期:" name="date" component={DatePicker} />
<Field label="">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);
.field-array-item [class*='ice-col-'] {
padding-top: 0;
padding-left: 0;
}
Fusion 表单组件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import { Button, Input, NumberPicker, Switch, Range, Select, DatePicker, TimePicker, Checkbox, Radio } from '@alifd/next';
const Option = Select.Option;
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class App extends Component {
async onSubmit(values) {
await sleep(300)
window.alert(JSON.stringify(values, 0, 2))
}
render() {
return (
<div>
<Form
onSubmit={this.onSubmit}
>
<Field label="Password:" name="password" htmlType="password" component={Input} placeholder="请输入密码" />
<Field label="NumberPicker:" name="numberPicker" defaultValue={3} component={NumberPicker} />
<Field label="Switch:" name="switch" component={Switch} valueName="checked" value={false} />
<Field label="Range:" name="range" defaultValue={30} scales={[0, 100]} marks={[0, 100]} component={Range} />
<Field label="Select:" name="select" component={Select} defaultValue='lucy'>
<Option value="jack">jack</Option>
<Option value="lucy">lucy</Option>
<Option value="disabled" disabled>disabled</Option>
<Option value="hugohua">hugohua</Option>
</Field>
<Field label="DatePicker:" name="date" component={DatePicker} />
<Field label="TimePicker:" name="time" component={TimePicker} />
<Field label="Checkbox:" name="checkbox" defaultValue={[]} component={Checkbox.Group}>
<Checkbox value="a">option 1 </Checkbox>
<Checkbox value="b">option 2 </Checkbox>
<Checkbox disabled value="c">option 3(disabled)</Checkbox>
</Field>
<Field label="Radio:" name="radio" component={Radio.Group}>
<Radio value="apple">apple</Radio>
<Radio value="banana">banana</Radio>
<Radio disabled value="cherry">cherry(disabled)</Radio>
</Field>
<Field label="" name="submit">
<Button htmlType="submit">Submit</Button>
</Field>
</Form>
</div>
);
}
}
ReactDOM.render((
<App />
), mountNode);