antd-better-form

Happy to use antd-better-form in the project based on ant-design ^_^

Usage no npm install needed!

<script type="module">
  import antdBetterForm from 'https://cdn.skypack.dev/antd-better-form';
</script>

README

antd-better-form

Happy to use antd-better-form in the project based on ant-design ^_^

ant-design 项目,使用 antd-better-form来快速构建表单。支持所有的ant-design输入型(data-entry)组件。

github 及 更新日志 在 0.1.0 版本之前暂不开放, 如有问题或者建议可以发邮件咨询:jiangzhifeng666@163.com, 需备注:antd-better-form 问题咨询

安装 Installation

# npm
npm install antd-better-form --save

使用 Usage

先看一个使用示例:

import React from "react";
import { Form, FormItem } from "antd-better-form";
import { Input, message } from "antd"; // 导入antd的Input组件

const MyForm = () => {
  return (
    <Form layout="inline">
      {$form => {
        const { $params, $valid, $reset, $getFirstError } = $form;

        const onSubmit = () => {
          if ($valid) {
            // submit your codes
          } else {
            message.error($getFirstError());
          }
        };

        return (
          <>
            <FormItem
              name="username"
              itemProps={{
                label: "Username"
              }}
            >
              <Input />
            </FormItem>

            <Button
              style={{ marginRight: "12px" }}
              type="primary"
              onClick={onSubmit}
            >
              确定
            </Button>
            <Button onClick={$reset}>重置</Button>
          </>
        );
      }}
    </Form>
  );
};

如果你觉得上面的方式不够优雅,你也可以使用withForm

import React from "react";
import { withForm, FormItem } from "antd-better-form";
import { Input, message } from "antd"; // 导入antd的Input组件

const MyForm = ({ $form }) => {
  const { $params, $valid, $reset, $getFirstError } = $form;

  const onSubmit = () => {
    if ($valid) {
      // submit your codes
    } else {
      message.error($getFirstError());
    }
  };

  return (
    <div>
      <FormItem
        name="username"
        itemProps={{
          label: "Username"
        }}
      >
        <Input />
      </FormItem>

      <Button style={{ marginRight: "12px" }} type="primary" onClick={onSubmit}>
        确定
      </Button>
      <Button onClick={$reset}>重置</Button>
    </div>
  );
};

export default withForm(MyForm);

FormItemantd-better-form 新增加的组件

只需要将ant-design的交互组件,嵌套在FormItem下,即可实现自动的表单状态同步。

withForm(Component)

withForm是基于 Form 封装的高阶组件,withForm 的第二个参数为可选配置,如果定义了该参数,会将配置传递给 Form 组件。

const FormComp = () => {
  // ...
};

export default withForm(FormComp, {
  layout: "horizontal" //该项将传递给Form组件
});

withForm同样支持装饰器语法

@withForm
class MyField extends Component {}

//or pass some default props
@withForm({
  layout: "inline"
})
class MyField extends Component {}

<Form />

支持传递的属性

$defaultValues

全局统一设置表单的默认值,不在需要在每个表单上设置单独$defaultValue。如果你两个地方都设置了同一个 FormItem 的默认值, $defaultValue 的优先级要高于 $defaultValues, 这也符合就近向上寻找的原则。

<Form $defaultValues={name: "ghostjzf"}>

或者

withForm(MyFormComp, {
  $defaultValues: {
    name: "ghostjzf"
  }
})
layout

表单布局, horizontalverticalinline, 默认值:horizontal

hideRequiredMark

隐藏所有表单项的必选标记, 默认值:false

labelAlign

label 标签的文本对齐方式, leftright, 默认值:right

labelCol

ant-design(3.14.0 新增,之前的版本只能设置到 FormItem 上。)label 标签布局,同 组件,设置 span offset 值,如 {span: 3, offset: 12} 或 sm: {span: 3, offset: 12}

wrapperCol

ant-design(3.14.0 新增,之前的版本只能设置到 FormItem 上。)需要为输入控件设置布局样式时,使用该属性,用法同 labelCol

colon

配置 Form.Item 的 colon 的默认值 (只有在属性 layout 为 horizontal 时有效)

$form

$form是当你是用withForm 或是 <Form />时为子组件注入的 props, 其中$form 包含以下参数:

  • $params 表单搜集的值的集合
  • $valid | $invalid$valid在表单项所有的表单项为合法的时候值为 true, 否则为 false. $invalid的值和 $valid 相反。
  • $getFirstError 获取表单项第一项错误
  • $reset 类型:Func, 重置表单,重置回默认值, 如果$reset传入参数,例如$reset('filedName')可以重置指定项的 value 值
  • $getField 获取指定表单项的工具, 包括:$setValue $reset
$form.$getField("name").$setValue("ghostjzf"); // 设置值
$form.$getField("name").$reset(); // 重置回默认值

<FormItem />

要实现将ant-design的交互组件的值能同步到 antd-better-form 的状态中,需要通过 FormItem 这个组件来实现中间态绑定。

它的作用有些类似 antd 中的getFieldDecorator方法,但是用法比getFieldDecorator更优雅,更 JSX 语法。FormItem完全是标签声明式用法,它是对 antd 的Form.Item组件的再次封装。

所以FormItem会完整实现Form.Item所可以显示的校验状态、错误暂时等 UI 变化。

如果给 FormItem 传递了多个子节点,则只会对第一个节点进行表单状态绑定!!

支持传递的属性

required

是否必填,默认值: false

name

设置输入项的 name 值,表单项将会以 name 作为 key 收集到 $form 的状态中.

$defaultValue

设置该表单项的默认值

$onChange

当 FormItem 的值发生变化时发生的回调

<FormItem
  $onChange={value => {
    console.log(value)
  }}
  itemProps={{
    label: "Username"
  }}
>
  <Input />
</FormItem>
validMessage

内置 required 错误提示信息

$validators

自定义设置校验方法, 返回错误提示信息,会覆盖 validMessage。

<FormItem $validators={value => {
  if (XXX) {
    return "XXX is valid"
  }
}}>
itemProps

该属性为要传递给Form.Item组件的配置项:

  • label label 标签的文本, 类型 string
  • colon 。配合 label 属性使用,表示是否显示 label 后面的冒号, 默认值 true
  • hasFeedback 配合 validateStatus 属性使用,展示校验状态图标,建议只配合 Input 组件使用,默认值 false
  • htmlFor 设置子元素 label htmlFor 属性, 类型 string
  • labelCol label 标签布局,同 组件,设置 span offset 值,如 {span: 3, offset: 12} 或 sm: {span: 3, offset: 12}。在 3.14.0 之后,你可以通过 Form 的 labelCol 进行统一设置。当和 Form 同时设置时,以 FormItem 为准。
  • wrapperCol 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol。在 3.14.0 之后,你可以通过 Form 的 wrapperCol 进行统一设置。当和 Form 同时设置时,以 FormItem 为准。
  • labelAlign 标签文本对齐方式,leftright, 默认值:right
<FormItem
  itemProps={{
    label: "Username",
    colon: false
  }}
>
  <Input />
</FormItem>
$parser

当用户在表单中进行输入时(主动更新视图),视图中的值在更新到状态模型中前,会经过 $parser 处理。

// 例如可以通过$parser属性来过滤前后输入空格
<FormItem name="fieldName" $parser={(value) => value.trim()}>
    //...
</FormItem>

注意,上述写法不会修改当前视图值,它仅仅影响状态模型中的值。

$formatter

表单初次渲染时更新视图的函数

例如当你需要在视图中默认带一个货币前缀时
<FormItem name="fieldName" $formatter={(value) => `¥${value}`}>
    //...
</FormItem>
valuePropName `changePropName

可以用来设置绑定到组件上的值或者值变动的事件回调。该项一般不需要设置,FormItem 已经针对 antd 中的所有 data-entry 型组件做了兼容处理。

支持的组件

AutoComplete
<FormItem
  name="autoComplete"
  itemProps={{
    label: "AutoComplete输入框"
  }}
>
  <AutoComplete />
</FormItem>
Checkbox

支持Checkbox.Group

<FormItem
  name="isChecked2"
  $defaultValue={["A", "C"]}
  itemProps={{
    label: "Check Group",
  }}
>
  <Checkbox.Group>
    <Checkbox value="A">A</Checkbox>
    <Checkbox value="B">B</Checkbox>
    <Checkbox value="C">C</Checkbox>
  </Checkbox.Group>
</FormItem>
Cascader
DatePicker

DatePicker TimePicker DatePicker.WeekPicker DatePicker.MonthPicker DatePicker.RangePicker 等几个日期类组件,都是深度结合了moment使用的。如果希望收集到表单中的值是格式化好的时间字符串,可以通过$parser $formatter实现:

<FormItem
  name="datepicker"
  $parser={moment => moment.format("YYYY-MM-DD")}
  $formatter={date => moment(date)}
>
  <DatePicker />
</FormItem>

对于DatePicker.RangePicker,由于其值是一个数组,所以需要这样处理:

<FormItem
  name="datepicker"
  $parser={moments => moments.map(moment => moment.format("YYYY-MM-DD"))}
  $formatter={dates => dates.map(date => moment(date))}
>
  <DatePicker.RangePicker />
</FormItem>
InputNumber
<FormItem
  name="inputNumber"
  itemProps={{
    label: "InputNumber数字框"
  }}
>
  <InputNumber />
</FormItem>
Input
Mention
Rate
<FormItem
  name="rate"
  $defaultValue={2.5}
  itemProps={{
    label: "Rate"
  }}
>
  <Rate allowHalf />
</FormItem>
Radio

支持Radio.Group

<FormItem
  name="radio"
  $defaultValue="B"
  itemProps={{
    label: "Radio"
  }}
>
  <Radio.Group>
    <Radio value="A">A</Radio>
    <Radio value="B">B</Radio>
    <Radio value="C">C</Radio>
  </Radio.Group>
</FormItem>
Switch

Switch Checkbox(不包括Checkbox.Group) Radio(不包括Radio.Group)三个组件,可以通过给FormItem传递$defaultValue={true}设置默认值:

<FormItem $defaultValue={true}>
  <Switch />
</FormItem>
Slider
<FormItem
  name="slider"
  $defaultValue={50}
  itemProps={{
    label: "Slider进度条"
  }}
>
  <Slider />
</FormItem>
Select
<FormItem
  name="select"
  $defaultValue="lucy"
  itemProps={{
    label: "Select下拉框"
  }}
>
  <Select style={{ width: 200 }}>
    <Select.Option value="jack">Jack</Select.Option>
    <Select.Option value="lucy">Lucy</Select.Option>
    <Select.Option value="disabled" disabled>
      Disabled
    </Select.Option>
    <Select.Option value="Yiminghe">yiminghe</Select.Option>
  </Select>
</FormItem>
TreeSelect
<FormItem
  name="treeSelect"
  itemProps={{
    label: "TreeSelect下拉框"
  }}
>
  <TreeSelect
    multiple
    showSearch
    style={{ width: 300 }}
    dropdownStyle={{
      maxHeight: 400,
      overflow: "auto"
    }}
    placeholder="Please select"
    allowClear
    treeDefaultExpandAll
  >
    <TreeNode value="parent 1" title="parent 1" key="0-1">
      <TreeNode value="parent 1-0" title="parent 1-0" key="0-1-1">
        <TreeNode value="leaf1" title="my leaf" key="random" />
        <TreeNode value="leaf2" title="your leaf" key="random1" />
      </TreeNode>
      <TreeNode value="parent 1-1" title="parent 1-1" key="random2">
        <TreeNode
          value="sss"
          title={
            <b
              style={{
                color: "#08c"
              }}
            >
              sss
            </b>
          }
          key="random3"
        />
      </TreeNode>
    </TreeNode>
  </TreeSelect>
</FormItem>
Transfer
TimePicker
Upload

Upload 组件会将 onChange 回调的对象同步到表单状态中,所以如果仅仅需要拿到上传成功后的服务端返回信息(比如上传后保存在服务器的 url),可以通过$parser 进行过滤:

<FormItem
  name="upload"
  $parser={({ file, fileList, event }) => {
    if (file.status == "done") {
      //render url form server
      return JSON.parse(file.response).data.url;
    }
  }}
>
  <Upload {...uplodConfig}>
    <Button>
      <Icon type="upload" /> Click to Upload
    </Button>
  </Upload>
</FormItem>

FAQ

在生产环境(NODE_ENV==='production')部分组件调用有异常?

如果在生产环境,发现例如Checkbox Radio Switch等组件无法正确捕获用户输入的值,这种情况一般是由于项目中使用了babel-plugin-import插件。

antd-better-form中是使用 import { Switch } from 'antd' 这种写法来调用 Switch 组件的,而babel-plugin-import插件会将项目源代码中的类似语句,替换成import Switch from 'antd/lib/switch'。这两种写法获取到的Switch其实并不是严格意义上的相等,前者是对后者的又一层导出封装。

而由于babel-plugin-import一般仅仅会配置成仅仅对项目代码进行处理,所以处于项目node_modules目录中的antd-better-form中的语句不会被处理。我们需要通过修改项目 webpack 配置的方式,来使babel-plugin-import插件能处理antd-better-form的代码。

可以编辑项目的 webpack 配置(只需要配置生产环境的构建配置即可),在rules模块下添加以下的代码:

{
    test: /\.(js|mjs)$/,
    include: /react-antd-formutil/, // 仅仅处理react-antd-formutil即可
    loader: require.resolve('babel-loader'),
    options: {
        babelrc: false,
        plugins: [[
            "import",
            {
                "libraryName": "antd"
            },
            "antd"
        ]]
    }
}