背景

基于ice/form实现的动态表单组件。

特性

安装

$ npm i ice-dynamic-form --save

快速上手

下面例子演示了如何创建一个简单的 form:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import FormDynamic from 'ice-dynamic-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>
        <h2>个人资料</h2>
        <FormDynamic
          onSubmit={this.onSubmit}
          components={{
            Input,
            TextArea: Input.TextArea,
            Button
          }}
          config={[{
            name: 'username',
            type: 'Input',
            label: '姓名:',
          },{
            name: 'age',
            type: 'Input',
            label: '年龄:',
          },{
            name: 'intro',
            type: 'TextArea',
            label: '简介:',
          }]}
          buttons={[{
            name: 'submit',
            type: 'Button',
            text: 'Submit',
            props: {
              htmlType: 'submit'
            }
          }]}
        />
      </div>
    );
  }
}

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

DEMO 列表

基本用法

Form 的基本用法

个人资料

姓名:
年龄:
简介:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import FormDynamic from 'ice-dynamic-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>
        <h2>个人资料</h2>
        <FormDynamic
          onSubmit={this.onSubmit}
          components={{
            Input,
            TextArea: Input.TextArea,
            Button
          }}
          config={[{
            name: 'username',
            type: 'Input',
            label: '姓名:',
          },{
            name: 'age',
            type: 'Input',
            label: '年龄:',
          },{
            name: 'intro',
            type: 'TextArea',
            label: '简介:',
          }]}
          buttons={[{
            name: 'submit',
            type: 'Button',
            text: 'Submit',
            props: {
              htmlType: 'submit'
            }
          }]}
        />
      </div>
    );
  }
}

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

重置表单

Form 子组件使用 function 渲染时,可以调用 FormCore 中的 API

个人资料

姓名:
年龄:
简介:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import FormDynamic from 'ice-dynamic-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>
        <h2>个人资料</h2>
        <FormDynamic
          onSubmit={this.onSubmit}
          components={{
            Input,
            TextArea: Input.TextArea,
            Button
          }}
          config={[{
            name: 'username',
            type: 'Input',
            label: '姓名:',
          },{
            name: 'age',
            type: 'Input',
            label: '年龄:',
          },{
            name: 'intro',
            type: 'TextArea',
            label: '简介:',
          }]}
          buttons={[{
            name: 'submit',
            type: 'Button',
            text: 'Submit',
            props: {
              htmlType: 'submit',
              style: {
                marginRight: '20px'
              }
            }
          },{
            name: 'reset',
            type: 'Button',
            text: 'Reset',
            props: {
              onClick: (formCore) => formCore.reset()
            }
          }]}
        >
        </FormDynamic>
      </div>
    );
  }
}

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

基础校验

校验规则可以写在 Form 属性上或者 Field 属性上

个人资料

姓名:
年龄:
简介:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import FormDynamic from 'ice-dynamic-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>
        <h2>个人资料</h2>
        <FormDynamic
          onSubmit={this.onSubmit}
          components={{
            Input,
            TextArea: Input.TextArea,
            Button
          }}
          config={[{
            name: 'username',
            type: 'Input',
            label: '姓名:',
          },{
            name: 'age',
            type: 'Input',
            label: '年龄:',
            rules: [{
              message: '年龄必填且大于18岁',
              required: true,
              validator: (rule, value) => value > 18
            }]
          },{
            name: 'intro',
            type: 'TextArea',
            label: '简介:',
          }]}
          buttons={[{
            name: 'submit',
            type: 'Button',
            text: 'Submit',
            props: {
              htmlType: 'submit',
              style: {
                marginRight: '20px'
              }
            }
          }]}
        >
        </FormDynamic>
      </div>
    );
  }
}

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

异步校验

异步校验,校验结果的 message 直接 callback 即可

个人资料

姓名:
年龄:
简介:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import FormDynamic from 'ice-dynamic-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>
        <h2>个人资料</h2>

        <FormDynamic
          onSubmit={this.onSubmit}
          components={{
            Input,
            TextArea: Input.TextArea,
            Button
          }}
          config={[{
            name: 'username',
            type: 'Input',
            label: '姓名:',
            rules: {
              async asyncValidator(rule, value, callback) {
                if (!value) {
                  callback('名称必填');
                } else {
                  await sleep(500);

                  if (~['john', 'paul', 'george', 'ringo'].indexOf(value)) {
                    callback('名称已存在');
                  } else {
                    callback(undefined)
                  }
                }
              }
            },
            props: {
              autoComplete: 'off'
            }
          },{
            name: 'age',
            type: 'Input',
            label: '年龄:',
            rules: [{
              message: '年龄必填且大于18岁',
              required: true,
              validator: (rule, value) => value > 18
            }]
          },{
            name: 'intro',
            type: 'TextArea',
            label: '简介:',
          }]}
          buttons={[{
            name: 'submit',
            type: 'Button',
            text: 'Submit',
            props: {
              htmlType: 'submit'
            }
          }]}
        />
      </div>
    );
  }
}

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

基础联动

基础联动

个人资料

姓名:
性别:
年龄:
简介:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Form, Field } from '@ice/form';
import FormDynamic from 'ice-dynamic-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>
        <h2>个人资料</h2>
        <FormDynamic
          rules={{
            username: [{
              required: true,
              min: 5,
              message: '姓名至少5个字符'
            }],
            age: [{
              required: true,
              message: '大于18岁',
              validator: (rule, value) => value > 18
            }]
          }}
          onSubmit={this.onSubmit}
          components={{
            Input,
            TextArea: Input.TextArea,
            Button,
            Radio,
            RadioGroup: Radio.Group
          }}
          config={[{
            name: 'username',
            type: 'Input',
            label: '姓名:',
            // 数组内为“且”还是“或”?
            // linkages数组内的条件为 或 的逻辑
            effects:[{
              // conditions数组内的为 且 逻辑
              conditions: [{
                symbol: "===",
                value: "Khaleesi"
              }],
              // action 为数组,条件满足是并发
              // show/hide/setValue
              actions: [{
                type: "setValue",
                target: "age",
                value: 28
              }]
            }]
          },{
            name: 'gender',
            type: 'RadioGroup',
            label: '性别:',
            effects: [{
              conditions: [{
                symbol: "===",
                value: "female"
              }],
              actions: [{
                type: "hide",
                target: "age",
              }]
            },{
              conditions: [{
                symbol: "!==",
                value: "female"
              }],
              actions: [{
                type: "show",
                target: "age",
              }]
            }],
            props: {
              dataSource: [{
                value: 'male',
                label: '男'
              },{
                value: 'female',
                label: '女',
              },{
                value: 'x',
                label: 'X'
              }]
            }
          },{
            name: 'age',
            type: 'Input',
            label: '年龄:',
            effects:[{
              conditions: [{
                symbol: ">=",
                value: "18",
                type: "number"
              }],
              actions: [{
                type: "show",
                target: "idCard"
              }]
            },{
              conditions: [{
                symbol: "<",
                value: "18",
                type: "number"
              }],
              actions: [{
                type: "hide",
                target: "idCard"
              }]
            }]
          },{
            name: 'idCard',
            label: '身份证:',
            type: 'Input',
            visible: false,
          },{
            name: 'intro',
            type: 'TextArea',
            label: '简介:'
          }]}
          buttons={[{
            name: 'submit',
            type: 'Button',
            text: 'Submit',
            props: {
              htmlType: 'submit'
            }
          }]}
        />
      </div>
    );
  }
}

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

复用 Field 组

可复用的 Field Group

Billing
Billing Street:
Billing City:
Billing Postal Code:
shipping
shipping Street:
shipping City:
shipping Postal Code:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import FormDynamic from 'ice-dynamic-form';
import { 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>
        <h1>可复用的 Field Group</h1>
        <FormDynamic
          components={{
            Address,
            Button
          }}
          config={[{
            name: 'billing',
            type: 'Address',
            label: 'Billing',
            props: {
              name: 'billing',
              label: 'Billing'
            }
          },{
            name: 'shipping',
            type: 'Address',
            label: 'shipping',
            props: {
              name: 'shipping',
              label: 'shipping'
            }
          }]}
          buttons={[{
            name: 'submit',
            type: 'Button',
            text: 'Submit',
            props: {
              htmlType: 'submit'
            }
          }]}
          onSubmit={this.onSubmit}
        />
      </div>
    );
  }
}

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

异步加载数据渲染

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

加载表单数据并且初始化(label支持自定义jsx)

姓名:
年龄:
简介:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import FormDynamic from 'ice-dynamic-form';
import { Button, Input } from '@alifd/next';

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

const load = async () => {
  await sleep(2000);
  return {
    username: 'erikras',
    age: 20,
    intro: '我是 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>
        
        <FormDynamic
          value={this.state.data}
          onSubmit={this.onSubmit}
          components={{
            Input,
            TextArea: Input.TextArea,
            Button
          }}
          config={[{
            name: 'username',
            type: 'Input',
            label: '姓名:',
          },{
            name: 'age',
            type: 'Input',
            label: '年龄:',
          },{
            name: 'intro',
            type: 'TextArea',
            label: '简介:',
          }]}
          buttons={[{
            name: 'submit',
            type: 'Button',
            text: 'Submit',
            props: {
              htmlType: 'submit'
            }
          }]}
        />
      </div>
    );
  }
}

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

表单布局

表单布局

姓名:
年龄:
简介:
介绍自己的经历介绍自己的经历介绍自己的经历介绍自己的经历
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import FormDynamic from 'ice-dynamic-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: 1,
      wrapperCol: 3,
      labelTextAlign: 'right'
    }
    return (
      <div>
        <FormDynamic
          layout={layout}
          onSubmit={this.onSubmit}
          rules={{
            username: [{
              required: true,
              min: 5,
              message: '姓名至少5个字符'
            }]
          }}
          components={{
            Input,
            TextArea: Input.TextArea,
            Button
          }}
          config={[{
            name: 'username',
            type: 'Input',
            label: '姓名:',
          },{
            name: 'age',
            type: 'Input',
            label: '年龄:',
          },{
            name: 'intro',
            type: 'TextArea',
            label: '简介:',
            tips: "介绍自己的经历介绍自己的经历介绍自己的经历介绍自己的经历"
          }]}
          buttons={[{
            name: 'submit',
            type: 'Button',
            text: 'Submit',
            props: {
              htmlType: 'submit'
            }
          }]}
        />
      </div>
    );
  }
}

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

demo

本 Demo 演示一行文字的用法。

用户名
个性签名
密码
密码必须同时包含字母数字符号(提示支持 HTML 标签)
输入值
sss
test23
test24
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import FormDynamic from 'ice-dynamic-form';
import { Button, Input } from '@alifd/next';

class DInput extends Component {
  constructor(props){
    super(props)
  }
  render(){
    let {htmlType,onChange,value,tips,...props} = this.props 
    return <input
      onChange={onChange}
      value={value}
      type={htmlType}
      {...props} />
  }
}
const Components = {
  DInput,
  Button,
  Input
}

class App extends Component {
    render() {
        return (
            <div>
                <FormDynamic
                  layout={{
                    labelTextAlign: 'left'
                  }}
                  components={Components}
                  value={{
                    userName: "jack",
                    a: "32"
                  }}
                  config={[{
                      name: 'userName',
                      type: 'DInput',
                      label: '用户名',
                      rules: [{
                          required: true,
                          min: 5,
                          message: '用户名至少 5 个字符'
                      }],
                      props: {
                          style: {
                              width: '160px',
                          }
                      }
                  },{
                      name: 'userdesc',
                      type: 'Input',
                      label: '个性签名',
                      props: {
                          style: {
                              width: '160px',
                          }
                      }
                  }, {
                      name: 'password',
                      label: '密码',
                      type: 'Input',
                      tips: '<b>密码必须同时包含字母数字符号(提示支持 HTML 标签)</b>',
                      rules: [{
                          required: true,
                          min: 6,
                          message: "密码至少6位"
                      }],
                      props: {
                          htmlType: 'password',
                          // 支持自定义
                          style: {
                              width: '160px',
                              border: '2px solid green'
                          }
                      }
                  }, {
                    name: "a",
                    type: "DInput",
                    label: "输入值",
                    // 数组内为“且”还是“或”?
                    // linkages数组内的条件为 或 的逻辑

                    effects:[{
                      // conditions数组内的为 且 逻辑
                      conditions: [{
                        symbol: "===",
                        value: "32"
                      }],
                      // action 为数组,条件满足是并发
                      // show/hide/setValue
                      actions: [{
                        type: "show",
                        target: "b",
                        // value: "312"
                      }]
                     }]
                  },{
                    name: "b",
                    type: "DInput",
                    label: "test2",
                    // 初始化问题
                    visible: false,
                    props: {
                      // visible: false,
                    }
                  },{
                    name: "b3",
                    type: "DInput",
                    label: "sss",
                    display: "hide",
                    status: "view",
                    props: {
                      value: "332255"
                    }
                  },{
                    name: "c",
                    type: "DInput",
                    label: "test23",
                  },{
                    name: "d",
                    type: "DInput",
                    label: "test24",
                    display: "hide"
                  }]}
                  effects = {[{
                    event:{
                      origin: "c"
                    },
                    options: [{
                      conditions: [{
                        symbol: "===",
                        value: "32"
                      }],
                      // action 为数组,条件满足是并发
                      // show/hide/setValue
                      actions: [{
                        type: "show",
                        target: "d",
                        // value: "312"
                      }]
                    }]
                  }]}
                  buttons={[{
                      name: 'loginAction',
                      type: 'Button',
                      text: '登录(成功后跳转首页)',
                      actionEvent: 'submit',             
                      props: {
                        className: 'login-btn',
                        style: {
                            marginRight: '10px'
                        },
                        htmlType: 'submit'
                      }
                  }, {
                      name: 'resetAction',
                      text: '重置',
                      actionEvent: 'reset',
                  }]} />
            </div>
        );
    }
}

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