zent
Version:
一套前端设计语言和基于React的实现
307 lines (216 loc) • 15.8 kB
Markdown
---
title: Form
subtitle: 表单
path: component/form
group: 数据
scatter: true
---
## Form 表单组件
1. [使用指南](#shi-yong-zhi-nan)
2. [表单校验](#biao-dan-xiao-yan)
3. [格式化 value](#ge-shi-hua-value)
4. [表单操作](#biao-dan-cao-zuo)
5. [其他](#qi-ta)
6. [组件原理](#zu-jian-yuan-li)
7. [其他说明](#qi-ta-shuo-ming)
8. [API](#api)
### 使用指南
#### 表单 `Form`
- `Form` 组件提供三种样式:`inline`,`horizontal`, `vertical`。
- 使用 `Form` 组件,必须先调用 `createForm` 方法包装,为表单注入 `zentForm` 属性,从而提供表单和表单元素的各种操作方法,详见 demo 和 [`zentForm` API](#zentform) 。
#### 表单域 `Field`
`Field` 组件本质上是一个辅助性的组件,不提供任何样式,只负责管理表单元素 value 值的生命周期和表单元素的 error 等信息。
- `Field` 必须要有 `name` 属性;
- `Field` 的展现形式由 `component` 属性传入的组件决定,`Form` 组件中内置了常用的表单元素组件 `FormInputField`,`FormSelectField`,`FormRadioGroupField`,`FormCheckboxField`,`FormCheckboxGroupField`,`FormNumberInputField`,`FormSwitchField`,`FormColorPickerField`,`FormDateRangePickerField`,也可以使用单独封装的自定义表单元素组件;
- `Form` 组件提供了 `getControlGroup` 方法,可以快速封装自定义表单元素组件,使用方法参考 demo 和 [`getControlGroup` API](#form-getcontrolgroup) 。
<!-- demo-slot-1 -->
<!-- demo-slot-2 -->
#### 使用 `getControlGroup` 封装自定义表单域
<!-- demo-slot-3 -->
#### 多个表单元素的封装
当一个 `Field` 里需要封装多个表单元素时,一般会将多个表单元素的 value 值封装在一个对象里传入到 `Field` 中。当无法使用 `getControlGroup` 满足封装要求时,可以自己封装组件,通过调用 `Field` 组件传入的 `onChange` 事件更改 `Field` 的 value。
⚠️注意:调用 `Field` 传入的 `onChange` 事件默认会覆盖原值,可以通过传入 `{ merge: true}` 参数可以部分覆盖 value 值。
<!-- demo-slot-4 -->
### 表单校验
#### 表单校验的使用
- `Field` 组件支持传入 `validations` 和 `validationErrors` 来指定校验规则和校验提示;
- `validations` 对象支持预置的内部校验规则(详见[内置 validation rules](#nei-zhi-validation-rules) ), 也支持传入自定义的校验函数,校验函数返回 `true` 时表示验证通过;
- 可以通过 `Form.createForm` 扩展内部校验规则,详见 [`Form.createForm` API](#form-createform) 。
<!-- demo-slot-5 -->
#### 表单校验时机
表单的默认校验时机是 value 值改变的时候。可以修改 `validateOnChange`,`validateOnBlur` 来改变校验时机,如在 blur 时再校验(一般用于Input输入框)。
<!-- demo-slot-6 -->
#### 异步校验
异步校验在 blur 时触发,如果需要在自定义组件中手动触发异步校验,需要自己调用`props.onBlur(event)`。 `value` 值可以直接传给 `event` ,或者作为 `event` 的属性传入。
如果在没有触发异步校验的情况下(比如没有对表单项进行过操作)直接提交表单时,默认不会触发异步校验,使用内置的`handleSubmit`方法可以在提交表单时触发从未进行的异步校验。
<!-- demo-slot-7 -->
### 格式化 `value`
`Form` 组件提供了 `format` 和 `nomalize` 方法 来对 `value` 进行格式化,它们的执行时机详见 [value 的生命周期](#field-zhong-value-de-sheng-ming-zhou-qi)。
<!-- demo-slot-8 -->
### 表单操作
- `Form.createForm` 为组件注入 `zentForm` 属性,提供了表单和表单元素的各种操作方法,如获取表单元素值,重置获取表单元素值等,详见 [`zenForm` API](#zentform)
- `Form` 组件内部对表单提交的过程也进行了封装,可以把异步提交过程封装在一个函数里并**返回 `Promise` 对象**,组件内部会根据 `Promise` 对象的执行结果分别调用 `onSubmitSuccess` 和 `onSubmitFail` 方法,同时更新内部维护的 `isSubmitting` 属性(可以通过 `zentForm.isSubmitting()` 得到)。
<!-- demo-slot-9 -->
<!-- demo-slot-10 -->
### 其他
#### `Form` 布局
<!-- demo-slot-11 -->
#### `Fieldset` 组件
<!-- demo-slot-12 -->
#### `FormSection` 组件
<!-- demo-slot-13 -->
#### `FieldArray` 组件
<!-- demo-slot-14 -->
### 组件原理
本组件核心由以下几部分组成:
- `createForm` 函数:用来构建一个高阶组件,其中维护了表单中的所有表单元素(`Field` 组件)实例。通过向子组件的 `props` 中注入 `zentForm` 属性来提供表单和表单元素的各种操作方法。
- `Form` 组件:作为整个表单的最顶层骨架,是对 `<form>` 标签的简单封装,定义了默认的 class 来提供基础样式。
- `Field` 组件:用来封装各种表单元素组件(如 `Input` 、 `Checkbox` 、`Select` 以及各种自定义组件)的一个高阶组件。其中维护了表单元素 value 值和校验错误等信息。Field 组件会向表单元素组件传入封装过的 `onChange` 、`onBlur` 回调和 `value` 、`error` 等表单元素需要的 props 。
具体的使用,详见 [API 说明](#api)。
### 其他说明
#### 封装自定义的表单元素组件
- `Field` 的展示完全由传入到 `component` 属性中的组件所控制。这个组件能够接收到所有从 `Field` 传入的 props (包括 `Field` 中构造的一些隐含的 props ,具体[`Form.Field` API](#form-field) )。
- 对于一些常用的 `zent` 表单组件, `Form` 组件已经使用了 `getControlGroup` 函数进行了封装。如果产品设计上有一些特殊的需求,或者需要封装自定义的组件,也可以直接使用或者参考 `getControlGroup`的方式来对组件进行封装, 参考 [demo 封装多个表单元素](#duo-ge-biao-dan-yuan-su-de-feng-zhuang)。
- **如果需要在一个 `Field` 中展示多个表单元素,可以将所有的表单元素封装在一个对象中传入 Field 的value 中。具体可以参考 [demo 封装多个表单元素](#duo-ge-biao-dan-yuan-su-de-feng-zhuang)。**
#### `Field` 中 `value` 的生命周期
- 表单元素的初始值需要通过在 `Field` 中指定 `value` 值传入。 `value` 值的生命周期如下图所示:
```
Field 中传入 value ---> 使用 format() 格式化 value ---> format 过的 value 传入 component 中渲染组件
↑ |
| ↓
| 用户操作改变 value
| |
| ↓
normalize 过的 value 写入 form 中维护, 用于数据提交 <--- 使用 normalize() 格式化 value
```
- 如果传入 `Field` 的 `value` 值是一个动态值,在外部改变 value 后会重新开始 value 的生命周期。
### API
#### **`Form`**
对 html 中 form 元素的一个简单封装, 提供默认的 className.
| 参数 | 说明 | 类型 | 默认值 | 是否必填 |
|------|------|------|--------|--------|
| className | 自定义额外类名 | string | `''` | 否 |
| prefix | 自定义前缀 | string | `'zent'` | 否 |
| vertical | 垂直排列布局 | boolean | `true` | 否 |
| horizontal | 水平排列布局 | boolean | `false` | 否 |
| inline | 行内排列布局 | boolean | `false` | 否 |
| onSubmit | 表单提交回调 | func(e:Event) | `noop` | 否 |
| style | 内联样式 | object | null | 否 |
#### **`Form.createForm`**
##### **使用方式:`Form.createForm(options)(FormComponent)`**
##### **`options`**
`options` 支持的配置项如下:
| 参数 | 说明 | 类型 | 是否必填 |
|------|------|------|------|
| formValidations | 用于添加自定义校验方法, 通过这种方式添加的方法在 validations 中使用时可以传额外的参数 | object | 否 |
⚠️注意:项目中的通用校验方法,可以通过在一个文件中定义公共的`formValidations`对象后引入。
##### **`createForm` 返回组件中可接收的 props**
`createForm` 方法构建了一个高阶组件,该组件可以定义了一些额外的 props 。
| 参数 | 说明 | 类型 | 是否必填 |
|------|------|------|------|
| onChange | 任意表单元素修改后触发的回调,参数为所有表单元素值的对象 | func(values: Object) | 否 |
| onSubmitSuccess | 提交成功后的回调,参数是 submit 函数中 promise 的返回值 | func(submitResult: any) | 否 |
| onSubmitFail | 提交失败后的回调,参数要么是 SubmissionError 的一个实例,要么是 undefined | func(submitError: SubmissionError) | 否 |
⚠️注意:想要获取被 createForm 包裹的 FormComponent 的实例,可以在 createForm 创建的组件上添加 ref 然后调用`getWrappedForm`方法获取到。
##### **`zentForm`**
经过 `Form.createForm` 包装的组件通过 props 被添加了 `zenForm` 属性, 可以通过 `this.props.zentForm` 访问, `zentForm` 提供的 API 如下:
| 参数 | 说明 | 类型 |
|------|------|------|
| getFormValues | 获取与 form 绑定的所有表单元素值 | func |
| getFieldError | 获取某个 Field 的错误信息, 没有报错信息返回空 | func(name: String) |
| setFormDirty | 设置所有 Field 的状态为非原始状态, 用于在提交表单时让 Field 把没有显示出来的错误显示出来 | func(isDirty: Boolean) |
| setFieldExternalErrors | 设置外部传入的错误信息(比如服务端校验错误), errors 的 key 为 Field 的 name , value 为错误文案 | func(errors: Object) |
| setFieldsValue | 设置表单 Field 的值为指定值 | func(data: Object) |
| resetFieldsValue | 把所有 Field 的值恢复到指定值或初始状态 | func(data: Object) |
| initialize | 设置表单 Field 初始值 | func(data: Object) |
| isValid | 表单的所有 Field 是否都通过了校验 | func |
| isSubmitting | 表单是否正在提交 | func |
| isValidating | 表单是否有 Field 在异步校验 | func |
| isFieldDirty | Field 是否变更过值 | func(name: String) |
| isFieldValidating | Field 是否在异步校验 | func(name: String) |
##### **`handleSubmit`**
`createForm` 还会为被包装的组件提供一个封装过的 `handleSubmit` 方法,具体使用可以参考[demo 表单操作](#biao-dan-cao-zuo)。
⚠️注意:如果希望在 `onSubmitFail` 回调中正确接收到 `error` 对象,需要在 `submit` 函数中抛出一个 `SubmissionError` 类型的对象
```jsx
const { SubmissionError } = Form;
submit() {
// do submit
...
throw new SubmissionError('error message');
}
onSubmissionFail(submissionError) {
if (submissionError && submissionError.errors === 'error message') {
// do something
}
}
```
#### **`Form.Field`**
所有需要维护 `value` 的表单元素组件都需要通过 `Field` 组件包装一下。
在 `Field` 组件上可以传入以下 props ,`component` 以外的其他 props (包括自定义的 props ),都会传入到 `component` 中所定义的表单元素组件中:
| 参数 | 说明 | 类型 | 是否必填 |
|------|------|------|------|
| name | 表单元素名 | string | 是 |
| component | 真正的表单元素组件,负责表单元素如何展示。可以是字符串(标准 html 元素名), 或者 React 组件 | string / React.Component | 是 |
| value | 表单元素初始值 | any | 是 |
| normalize | onChange 或者 onBlur 后格式化表单元素值 | func(value, previousValue, nextValues, previousValues) | 否 |
| format | 渲染前格式化表单元素值, 不影响真正存储的表单元素值 | func(value, previousValue, nextValues, previousValues) | 否 |
| onChange | value 值修改后的回调,会在 Field 中封装一层。(自定义组件需要自己调用由 Field 组件封装后传入的 `props.onChange()` 后才会执行) | func(event, newValue, previousValue, preventSetValue) | 否 |
| onBlur | blur 后的回调(会在 Field 中封装一层) | func(event, newValue, previousValue, preventSetValue) | 否 |
| onFocus| focus 后的回调(会在 Field 中封装一层) | func(event) | 否 |
| validations | 定义表单元素校验方法 | object | 否 |
| validationErrors | 定义表单元素检验方法对应的出错信息 | object | 否 |
| validateOnChange | 是否在触发change事件时执行表单校验 | boolean | 否 |
| validateOnBlur | 是否在触发blur事件时执行表单校验 | boolean | 否 |
| clearErrorOnFocus | 是否在触发focus事件时清空错误信息 | boolean | 否 |
| asyncValidation | 异步校验 func, 需要返回 Promise | func(values, value) | 否 |
除了上述参数之外, `Field` 组件会隐含地向被包裹的表单元素组件中传入以下 props :
| 参数 | 说明 | 类型 |
|------|------|------|
| isDirty | 表单元素值被改变过 | boolean |
| isActive | 表单元素为input且获得了焦点 | boolean |
| error | 第一个校验错误文本信息(没有报错时为 null ) | string / Null |
| errors | 校验错误文本信息数组(没有错误时为空数组) | array |
##### **获取 `Field` 对应 `component` 的实例**
可以通过在 `Field` 上加上 `ref`,然后调用 `getWrappedComponent` 方法来获取。
```
<Field
ref={ref => { this.field = ref }}
component={XxxComponent}
...
/>
const component = field.getWrappedComponent();
```
#### **`Form.getControlGroup`**
`getControlGroup` 是一个用来快速封装自定义组件的函数,它返回一个满足通用布局与样式要求(左侧标签 、右侧表单元素)的stateless functional component 。同时支持将 `Field` 中的 错误提示信息展示出来。
封装过的组件支持在 `Field` 上额外传入以下参数:
| 参数 | 说明 | 类型 | 是否必填 |
|------|------|------|------|
| label | 表单元素的label | string / React.Component | 否 |
| className | 添加到control-group 上的额外类名,可以用来覆盖子元素的样式 | string | 否 |
| helpDesc | 表单元素的说明性文字 | string / React.Component | 否 |
| notice | 表单元素的重要提示性文字 | string / React.Component | 否 |
| required | 为 true 时会在 label 前添加红色的"*" | boolean | 否 |
##### **获取 `Control` 组件实例**
参照上方获取 `Field` 对应 `component` 的实例,然后调用 `getControlInstance` 方法。
```jsx
const component = field.getWrappedComponent().getControlInstance();
```
#### **内置 validation rules**
可以直接在 `Field` 的 `validations` 属性中使用,使用方法参考[demo 常用表单校验](#biao-dan-xiao-yan-de-shi-yong)。内置规则如下:
| 规则名 | 说明 | 可传参数 |
|------|------|------|
| required | 是否必填 | 任意,传 true 是为了表意,传其他值也是当作必填,下同 |
| isExisty | 是否非 null ,非 undefined | 任意 |
| matchRegex | 是否匹配指定正则表达式 | Regex |
| isEmail | 是否邮件类型字符串 | 任意 |
| isUrl | 是否 url 类型 | 任意 |
| isTrue | 是否true | 任意 |
| isFalse | 是否false | 任意 |
| isNumeric | 是否数字类型 | 任意 |
| isInt | 是否整数 | 任意 |
| isFloat | 是否小数 | 任意 |
| isLenght | 字符串或数组是否为指定长度 | 长度值(Number) |
| equals | 是否与指定值相等 | 指定值 |
| equalsField | 是否与表单中的其他元素值相等 | 其他 Field 的name(String) |
| maxLength | 字符串或数组不能超过指定长度 | 长度值(Number) |
| minLength | 字符串或数组不能小于指定长度 | 长度值(Number) |