IceForm 表单组件

如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。

安装方法

  1. 在命令行中执行以下命令npm install @ice/form@0.1.5 -S

表单组件

参数(Props)

参数名说明必填类型默认值备注
initialValues表单初始值Nobject{}-
onSubmitsubmit函数Yfunction--
onChange表单变化回调Nfunction-function(values: object, item: object) => void 参数: values: {object} 表单数据 item: {object} 详细 item.name: {string} 变化的组件名 item.value: {string} 变化的数据
rules校验规则Nobject{}-
effects联动规则Narray[]-
layout表单布局Nobject-
renderField自定义 Field 布局Nfunction-function({label, component, error}) => dom 参数: label: {string/element} Field 的 label component: {string/function} 待渲染的控件 error: {string/element} Field 错误提示信息

其他属性比如 styleclassName 等均会传递到 form 标签上。

layout 是个对象,包含 4 个属性:

  1. {
  2. labelAlign: 'left', // label 的位置,'left'、'top',默认 'left'
  3. labelTextAlign: 'right', // label 文字对齐方式,'left'、'right',默认 'right'
  4. labelCol: 1, // label 占的栅格宽度,共 12 等分,默认 2
  5. wrapperCol: 3, // 控件占的栅格宽度,共 12 等分,默认 6
  6. }

rules 是一个 Object,key<Field>name 属性值,value 是个数组,数组里面的每一项是一个校验规则,参考 async-validator

  1. <Form
  2. onSubmit={this.onSubmit}
  3. style={{color: '#ee7893'}}
  4. rules={{
  5. username: [{
  6. required: true,
  7. min: 5,
  8. message: '姓名至少5个字符'
  9. }],
  10. age: [{
  11. required: true,
  12. message: '年龄必填'
  13. }]
  14. }}
  15. >
  16. <Field label="姓名:" name="username" component="input" type="text" />
  17. <Field label="年龄:" name="age" component='input' type="number" />
  18. </Form>

effects 是个数组,写法如下:

  1. <Form
  2. onSubmit={this.onSubmit}
  3. effects={[
  4. {
  5. field: 'username',
  6. handler: formCore => {
  7. if (formCore.getFieldValue('username') === 'ice') {
  8. formCore.setFieldValue('age', 2)
  9. }
  10. }
  11. }
  12. ]}
  13. >
  14. <div>Hello Form</div>
  15. <Field label="姓名:" name="username" component="input" type="text" />
  16. <Field label="年龄:" name="age" component='input' type="number" />
  17. <button type="submit">Submit</button>
  18. </Form>

监听该 fieldonChange 事件,然后设置其他表单项的数据,从而达到联动效果。handler 的参数是 formCore 对象,该对象暴露一些 api 可以设置 value、error、show/hide 等。

Field 组件

参数名说明必填类型默认值备注
label表单项的 labelNstring/element--
name表单项的 nameYstring--
component表单类型,原生 html 标签或者三方组件Nstring/function-'input' 'textarea' Input Radio
value表单项的值N-''-
rules校验规则Nobject or array--
effects联动规则Nobject--
visible显示隐藏当前 FieldNbooleantruetrue/false
setValueFormatter格式化控件渲染值Nfunctionfunction(savedValue) => renderValue
getValueFormatter格式化控件提交值Nfunctionfunction(renderValue) => savedValue
layout设置当前 Field 的布局Nobject同 Form layout当前 Field 的 layout 会覆盖 Form 的 layout
tips提示信息Nstring
valueName控件值的名称,比如,radio 的 valueName 为 'checked',value 为 true/falseNstring比如 Fusion 的 Switch 组件
errorRender自定义 error 渲染Nfunction(error) {}
onChange自定义 onChange 函数Nfunction() {}默认情况下已处理表单的 onChange(eventOrValue) 事件,如果接入的三方控件 onChange 的第一个参数不是 event 或者 value,可以主动设置对应的值。比如,接入控件的 onChange(xxx, value) 第二个参数才是 value,则可以手动设置 formCore.setValue(fieldname, value)

styleclassName 属性会传递到 Field 最外层 dom 上,其他属性会传递到 component 上,如果没有 component 但有 children,则属性传递到 children 上。

Fieldruleseffects 不需要 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 组件

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>

codeformCore

formCore 会暴露一些 API,使用这些 API 可以获取、设置表单的数据、状态等。

  • getFieldValue(name):获取某一 Field 的值

  • setFieldValue(name, value):设置某一 Field 的值

  • getValues():获取表单的 values

  • setValues(values, runEffects):设置表单的 values,runEffects 为 Boolean,表示设置 values 之后是否需要执行表单的 effects,默认 false

  • getFieldError(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 相等

延伸阅读

开发 @ice/form 表单背景

对于前端,表单开发是一件特别繁琐的事情,尤其在中后台业务中,大家常常会被各种五花八门的表单折磨,又不得不面对现实地去寻找最佳方案,但最终都会发现过度设计的表单组件性能不好,使用简单的表单组件还是需要写大量的业务代码。经过长期的积累以及在社区的调研,我们开发了一个表单组件帮助大家快速地创建一个高性能表单。

组件特性

  • 内部几乎无依赖,体积小

  • 内部管理表单状态,提升开发效率

  • 使用观察者模式提升表单性能

  • 强大的校验以及声明式联动

  • 可结合第三方组件库(Next、Antd)

  • 可自定义 Field

架构方案

IceForm 表单组件 - 图1

如上图所示,整个表单的数据都放在 FormCore 这一层,同时 FormCore 会暴露一些 API,以便获取、设置、处理数据。Form、Field 组件通过 Sub/Pub 模式与 FormCore 通信,FormCore 通知组件何时重新渲染。表单提供了校验、联动以及结合 Fusion、Antd 三方组件库使用等能力。

竞品对比

NoForm

NoForm 是一个表单操作(比如说校验、提交、联动等)抽象到上层,下层又包装了 Next、Antd 等组件,UI 上的能力较强,也封装了一些常用的布局,但功能能力较弱,用户实现复杂逻辑还是需要写很多代码。

Formikreact-final-form

这两个组件有一些共性,都是通过 render props 的方式实现了复杂的状态管理,在性能上也非常地卓越,在社区得到了大量的好评,但在联动上的能力较弱(目前只能更新 value),而且如果要集成 Next 或者 Antd 需要将库的表单组件都封装成 Field,成本较高。

相关链接

代码示例

基本用法

Form 的基本用法

IceForm 表单组件 - 图2

查看源码在线预览

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

IceForm 表单组件 - 图3

查看源码在线预览

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 属性上

IceForm 表单组件 - 图4

查看源码在线预览

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 即可

IceForm 表单组件 - 图5

查看源码在线预览

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);

基础联动

基础联动

IceForm 表单组件 - 图6

查看源码在线预览

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);

省市联动

省市联动

IceForm 表单组件 - 图7

查看源码在线预览

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

IceForm 表单组件 - 图8

查看源码在线预览

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);

自定义 Field 布局

通过 Form 的 renderField 自定义 Field 布局

IceForm 表单组件 - 图9

查看源码在线预览

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);

格式化 Field 值

Format Feild's value

IceForm 表单组件 - 图10

查看源码在线预览

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);

复用 Field 组

IceForm 表单组件 - 图11

查看源码在线预览

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);

异步加载数据渲染

异步加载数据进行首屏渲染

IceForm 表单组件 - 图12

查看源码在线预览

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);

Dialog 表单

Form 结合对话框

IceForm 表单组件 - 图13

查看源码在线预览

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);

原生 html 标签

使用原生 html 标签

IceForm 表单组件 - 图14

查看源码在线预览

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);

异步更新 Form props

IceForm 表单组件 - 图15

查看源码在线预览

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);

表单布局

表单布局

IceForm 表单组件 - 图16

查看源码在线预览

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

IceForm 表单组件 - 图17

查看源码在线预览

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 自定义标签,给每个标签增加必填符号

IceForm 表单组件 - 图18

查看源码在线预览

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

IceForm 表单组件 - 图19

查看源码在线预览

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;
}

第一行两个 Field

IceForm 表单组件 - 图20

查看源码在线预览

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 ;
}

FieldArray 组件

IceForm 表单组件 - 图21

查看源码在线预览

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 表单组件

Fusion 表单组件

IceForm 表单组件 - 图22

查看源码在线预览

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);

相关区块

IceForm 表单组件 - 图23

暂无相关区块