表单 Form
具有数据收集、校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。
安装
npm install @arco-design/web-react@2.66.18
在入口引入 @arco-design/web-react/dist/css/arco.css,用 ConfigProvider 设置语言。React 19 项目还需在入口最先引入 @arco-design/web-react/es/_util/react-19-adapter,否则 Message、Modal 等命令式 API 无法渲染。
示例
- 基础用法:非受控模式的用法。
- 表单布局:
Form支持三种排列方式: horizontal 水平排列 (默认)、 vertical 垂直排列、 inline 行内排列 - 受控表单:可以在
Form.Item传入field属性,即可使控件变为受控组件,表单项的值都将会被Form收集。 <ol> <li> 受控模式下Form.Item会接管控件,自动给表单控件添加相应的value(或triggerPropName指定的其他属性)和onChange(或trigger指定的其他属性),所有的数据收集都由Form内部完成。 </li> <li> 受控下不要为表单控件添加defaultValue。默认值可以通过Form的initialValues或Form.Item的initialValue来设置。 </li> </ol> - 表单方法调用:在函数式组件里可以使用
Form.useForm获取表单实例。通过该实例调用表单方法,例如设置表单字段值,重置表单等。 在类组件里可以使用ref获取表单实例。 - 控制表单项错误状态:通过
setFields方法的error参数,可以在外部控制表单项的错误状态。 - 复杂类型的数据:通过设置
field为a.b.c格式,可以得到{a:{b:{c: xx}}}。 - 表单控件嵌套:
Form.Item可以互相嵌套。 - 表单控件联动:可以通过
shouldUpdate实现控件间的联动。 - 动态增减表单项:通过
Form.List管理数组类型的表单结构。设置校验规则时Form.List会渲染出额外的DOM节点用来展示校验信息,如果不需要可以传入noStyle=false,并通过Form.useFormState自定义校验信息的展示。 - 动态增减嵌套类型的表单项:通过
Form.List管理数组类型的表单结构。可以通过对field进行一些处理,实现动态增减复杂类型的表单项 - 自定义表单控件:
Form.Item会给自己的直接子节点(必须是唯一子节点)传递onChange和value属性,自定义控件只有在调用这个onChange之后,自己的值才能被Form.Item收集到 。 - 处理表单项的值:首先,在使用受控表单时,所有表单项的值在改变时,都会被
Form收集到。如果需要对表单项的值进行处理后再存储在Form中,可以使用normalize属性。与之相对,在进行表单项的渲染时,都会从Form中取值,并作为value属性传递给对应的表单项,如果需要对从Form中取出的值进行转换再传递给表单项,可以使用formatter属性。注意:form.setFieldValue不会触发normalize。 - 注册表单:填写必须的信息以注册新用户
- 全局禁用:给
Form组件设置disabled可以全局禁用所有表单控件。 - 不同尺寸:通过
size属性可以设置不同尺寸的表单 - 自定义表单校验状态:
Form.Item支持通过 hasFeedbackvalidateStatus和help属性自定义表单校验状态及校验文案。 - 表单校验:可以通过
form.validate方法进行表单字段的校验。可以通过参数指定校验特定字段。 - 表单异步校验:在
rules中自定义validator方法,并返回一个Promise即可实现表单的异步校验。 p.s: 如果用lodash.debounce不生效,建议使用debounce.promise,它返回的是一个promise。 - 表单校验信息模板:可以通过
validateMessages属性设置校验信息提示模板(2.32.0支持)。示例 也可以在ConfigProvider组件的componentConfig参数,为全局的Form组件设置validateMessages。 - 滚动到指定表单字段
- 表单校验失败不阻塞提交:可以通过
validateLevel设置表单校验失败是显示为warning状态,不阻塞表单提交。 - 多表单联动:可以通过
Form.Provider组件管理多个表单的数据。2.30.0支持。需要想要获得对应的表单实例,需要为Form组件设置id属性。 - useWatch:可以通过
Form.useWatch监听表单内部字段值的变动。(2.33.0支持) - 获取表单上下文:在函数式组件里可以使用
Form.useFormContext获取 Form 组件上下文,便于表单控件的封装。(version2.33.0,isSubmittingin2.44.0).isSubmitting仅在通过type=submit的button触发表单Form的onSubmit属性进行提交时有效。如果onSubmit内部包含异步逻辑,请返回一个 Promise. - useFormState:可以通过
Form.useFormState监听表单内部状态的变动。(2.44.0支持)
示例来自 Arco Design 官方仓库(MIT),固定在 npm 2.66.18 对应的提交;部分示例使用在线图片或上传地址,预览中网络请求被禁用。
API
Form
| 参数名 | 描述 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| disabled | 统一配置表单控件是否可用 | boolean | - | - |
| id | 设置后,会作为表单控件 id的前缀。 | string | - | - |
| labelAlign | 标签的文本对齐方式 | 'left' | 'right' | right | - |
| layout | 表单的布局,有三种布局,水平、垂直、多列。 | 'horizontal' | 'vertical' | 'inline' | horizontal | - |
| requiredSymbol | 是否在 required 的时候显示加重的红色星号,设置 position 可选择将星号置于 label 前/后 | boolean | { position: 'start' | 'end' } | true | position in 2.24.0 |
| size | 不同尺寸。 | 'mini' | 'small' | 'default' | 'large' | - | - |
| colon | 是否显示标签后的一个冒号,优先级小于 Form.Item 中 colon 的优先级。(ReactNode in v2.41.0) | boolean | ReactNode | - | - |
| className | 节点类名 | string | string[] | - | - |
| form | form | FormInstance<FormData, FieldValue, FieldKey> | - | - |
| initialValues | 设置表单初始值 | Partial<FormData> | - | - |
| labelCol | <label>标签布局,同<Grid.Col>组件接收的参数相同,可以配置span和offset值,会覆盖全局的labelCol设置 | ColProps | { span: 5, offset: 0 } | - |
| onChange | 表单项值改变时候触发。和 onValuesChange 不同的是只会在用户操作表单项时触发 | (value: Partial<FormData>, values: Partial<FormData>) => void | - | - |
| onValuesChange | 任意表单项值改变时候触发。第一个参数是被改变表单项的值,第二个参数是所有的表单项值 | (value: Partial<FormData>, values: Partial<FormData>) => void | - | - |
| scrollToFirstError | 验证失败后滚动到第一个错误字段。(ScrollIntoViewOptions 类型在 2.19.0 开始支持) | boolean | ScrollIntoViewOptions | - | - |
| style | 节点样式 | CSSProperties | - | - |
| validateMessages | 校验提示信息模板 demo | Partial<{[key in keyof ValidateMessagesTemplateType]: ValidateMessagesTemplateType[key] extends string? ValidateMessagesTemplateType[key] | ((data, { label }) => any): Partial<Record<keyof ValidateMessagesTemplateType[key], string | ((data, { label }) => any)>>;}> | - | 2.32.0 |
| validateTrigger | 触发验证的时机。 | string | string[] | onChange | 2.28.0 |
| wrapper | 配置最外层标签,可以是 html 标签或是组件 | ComponentType | form | - |
| wrapperCol | 控件布局,同labelCol的设置方法一致,会覆盖全局的wrapperCol设置,ColProps | ColProps | { span: 19, offset: 0 } | - |
| wrapperProps | 配置 wrapper 之后,可以传一些参数到 wrapper 上。 | IndexedObject | - | - |
| onSubmit | 数据验证成功后回调事件 | (values: FormData) => void | - | - |
| onSubmitFailed | 数据验证失败后回调事件 | (errors: { [key: string]: FieldError }) => void | - | 2.21.0 |
Form.Item
| 参数名 | 描述 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| disabled | 是否禁用,优先级高于 Form 的 disabled 属性 | boolean | - | - |
| hasFeedback | 是否显示校验图标,配置 validateStatus 使用。 | boolean | - | - |
| hidden | 隐藏表单项. 表单字段值仍然会被获取 | boolean | - | 2.29.0 |
| required | 是否必选,会在 label 标签前显示加重红色符号,如果这里不设置,会从 rules 中寻找是否是 required | boolean | - | - |
| trigger | 接管子节点,搜集子节点值的时机。 | string | onChange | - |
| triggerPropName | 子节点被接管的值的属性名,默认是 value,比如 <Checkbox> 为 checked。 | string | value | - |
| labelAlign | 标签的文本对齐方式,优先级高于 Form | 'left' | 'right' | right | - |
| layout | 布局 | 'horizontal' | 'vertical' | 'inline' | - | - |
| requiredSymbol | 是否在 required 的时候显示加重的红色星号,设置 position 可选择将星号置于 label 前/后 | boolean | { position: 'start' | 'end' } | true | position in 2.24.0 |
| validateStatus | 校验状态 | 'success' | 'warning' | 'error' | 'validating' | - | - |
| colon | 是否显示标签后的一个冒号,优先级小于 Form.Item 中 colon 的优先级。(ReactNode in v2.41.0) | boolean | ReactNode | - | - |
| extra | 额外的提示内容。 | ReactNode | - | - |
| help | 自定义校验文案 | ReactNode | - | - |
| label | 标签的文本 | ReactNode | - | - |
| children | ReactNode 类型与函数类型的 children | React.ReactNode | FormItemChildrenFn<FormData, FieldValue, FieldKey> | - | - |
| className | 节点类名 | string | string[] | - | - |
| dependencies | 设置依赖字段。当依赖的字段值改变时,触发自身的校验。如果是想动态渲染某个表单控件/表单区域,使用 shouldUpdate | string[] | - | 2.40.0 |
| field | 受控组件的唯一标示 | FieldKey | - | - |
| initialValue | 设置控件初始值.(初始值,请不要使用受控组件的defaultValue了) | FieldValue | - | - |
| labelCol | <label>标签布局,同<Grid.Col>组件接收的参数相同,可以配置span和offset值,会覆盖全局的labelCol设置 | ColProps | - | - |
| normalize | 将控件的 value 进行一定的转换再保存到form中。 | (value: FieldValue | undefined,prevValue: FieldValue | undefined,allValues: Partial<FormData>) => any | - | - |
| noStyle | 不渲染任何外部标签/样式,只进行字段绑定。注意: 设置该属性为true时,该字段若未通过校验,错误信息将不会显示出来。可以传入对象,并设置 showErrorTip( 2.5.0 开始支持) 为true,错误信息将会展示在上层 formItem 节点下。 | boolean | { showErrorTip: boolean } | - | - |
| rules | 受控模式下的验证规则,RulesProps | RulesProps<FieldValue>[] | - | - |
| shouldUpdate | 是否在其他控件值改变时候重新渲染当前区域。设置为true时候,表单的任意改变都会重新渲染该区域。 | | boolean| ((prevValues: Partial<FormData>,currentValues: Partial<FormData>,info: {isFormList?: boolean;field?: FieldKey | FieldKey[];isInner?: boolean;}) => boolean) | - | - |
| style | 节点样式 | CSSProperties | - | - |
| tooltip | 提示文本 | ReactNode | (TooltipProps & { icon?: ReactElement }) | - | 2.43.0 |
| validateTrigger | 触发验证的时机。取值和跟包裹的控件有关系,控件支持的触发事件,都可以作为值。例如Input支持的 onFocus、 onBlur、 onChange 都可以作为 validateTrigger 的值。传递为 [] 时,仅会在调用表单 validate 方法时执行校验规则。 | string | string[] | onChange | - |
| wrapperCol | 控件布局,同labelCol的设置方法一致,会覆盖全局的wrapperCol设置,ColProps | ColProps | - | - |
| formatter | 将Form内保存的当前控件对应的值进行一定的转换,再传递给控件。 | (value: FieldValue | undefined) => any | - | 2.23.0 |
| getValueFromEvent | 指定在子节点触发onChange事件时如何处理值。(如果自定义了trigger属性,那么这里的参数就是对应的事件回调函数的参数类型) | (...args) => FieldValue | - | 2.23.0 |
Form.List
| 参数名 | 描述 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| noStyle | FormItemProps['noStyle'].rules 存在时默认为 false(需要渲染校验信息),否则默认为 true | FormItemProps['noStyle'] | - | 2.46.0 |
| field | 字段名 | FieldKey (必填) | - | - |
| initialValue | 初始值 | SubFieldValue[] | - | 2.22.0 |
| rules | 受控模式下的验证规则,RulesProps | RulesProps<SubFieldValue[]>[] | - | 2.46.0 |
| children | 函数类型的 children | (fields: { key: number; field: SubFieldKey }[],operation: {add: (defaultValue?: SubFieldValue, index?: number) => void;remove: (index: number) => void;move: (fromIndex: number, toIndex: number) => void;}) => React.ReactNode | - | - |
Form.Provider(2.30.0)
| 参数名 | 描述 | 类型 | 默认值 |
|---|---|---|---|
| onFormSubmit | 包裹的任意 Form 组件触发提交时,该方法会被调用 | (id: string | undefined,values,{forms,}: {forms: {[key: string]: FormInstance;};}) => void | - |
| onFormValuesChange | 包裹的任意 Form 组件的值改变时,该方法会被调用 | (id: string | undefined,changedValues,{forms,}: {forms: {[key: string]: FormInstance;};}) => void | - |
FormInstance
export type FormInstance<
FormData = any,
FieldValue = FormData[keyof FormData],
FieldKey extends KeyType = keyof FormData
> = Pick<
Store<FormData, FieldValue, FieldKey>,
| "getFieldsValue"
| "getFieldValue"
| "getFieldError"
| "getFieldsError"
| "getTouchedFields"
| "getFields"
| "setFieldValue"
| "setFieldsValue"
| "setFields"
| "resetFields"
| "clearFields"
| "submit"
| "validate"
| "getFieldsState"
> & {
scrollToField: (field: FieldKey, options?: ScrollIntoViewOptions) => void;
// arco 内部使用,业务万不可调用
getInnerMethods: (
inner?: boolean
) => InnerMethodsReturnType<FormData, FieldValue, FieldKey>;
};
InnerMethodsReturnType
export type InnerMethodsReturnType<
FormData = any,
FieldValue = FormData[keyof FormData],
FieldKey extends KeyType = keyof FormData
> = Pick<
Store<FormData, FieldValue, FieldKey>,
| "registerField"
| "registerStateWatcher"
| "registerFormWatcher"
| "registerWatcher"
| "innerSetInitialValues"
| "innerSetInitialValue"
| "innerSetCallbacks"
| "innerSetFieldValue"
| "innerGetStore"
| "innerGetStoreStatus"
| "innerCollectFormState"
| "innerGetFieldValue"
>;
ComponentType
export type ComponentType =
| keyof JSX.IntrinsicElements
| React.ComponentType<any>;
IndexedObject
export type IndexedObject = {
[key: string]: any;
};
FieldError
export type FieldError<FieldValue = any> = {
value?: FieldValue;
message?: ReactNode;
type?: string;
requiredError?: boolean;
};
FormItemChildrenFn
export type FormItemChildrenFn<
FormData = any,
FieldValue = FormData[keyof FormData],
FieldKey extends KeyType = keyof FormData
> = (
formData: any,
form: FormInstance<FormData, FieldValue, FieldKey>
) => React.ReactNode;
Rules
底层使用 b-validate。
export interface RulesProps {
// 触发校验的时机
validateTrigger?: string | string[];
// 校验失败时候以 `error` 或 `warning` 形式展示错误信息。当设置为 `warning` 时不会阻塞表单提交
validateLevel?: 'error' | 'warning';
required?: boolean;
type?: string;
length?: number;
// Array
maxLength?: number;
minLength?: number;
includes?: boolean;
deepEqual?: any;
empty?: boolean;
// Number
min?: number;
max?: number;
equal?: number;
positive?: boolean;
negative?: boolean;
// Object
hasKeys?: string[];
// String
match?: RegExp;
uppercase?: boolean;
lowercase?: boolean;
// Boolean
true?: boolean;
false?: boolean;
// custom
validator?: (value, callback: (error?: ReactNode) => void) => void;
message?: string;
}
this.form
通过ref可以拿到this.form,this.form包含了表单的验证、设值等常用操作。
<Form ref={(ref) => (this.form = ref)}>
<FormItem>...</FormItem>
</Form>
| 方法 | 描述 | 类型 | 版本 |
|---|---|---|---|
| validate | 校验并获取表单输入域的值与 Errors,如果不设置 fields 的话,会验证所有的 fields。支持 callback 和 promise 两种使用方法。 | Function(fields?: string[], callback(errors, values) => void) | |
| submit | 提交表单 | Function | |
| setFieldValue | 设置一个表单控件的值 | Function(field: string, value) | |
| setFields | 设置一组表单控件的值和报错。 | Function({ [field]: string: { value: any, error: FieldError } }) | |
| setFieldsValue | 设置多个表单控件的值 | Function({[field]: string, value}) | |
| getFieldValue | 获取一个表单控件的值。请不要把返回值直接作为 useEffect 的依赖,其返回值会被深克隆,引用地址会发生改变 | Function(field: string) | |
| getFields | 获取全部表单项的值,包括被创建后销毁的表单项。请不要把返回值直接作为 useEffect 的依赖,其返回值会被深克隆,引用地址会发生改变 | Function | |
| getFieldsValue | 获取一组表单控件的值,如果不设置 fields 的话,会获取所有的 fields。请不要把返回值直接作为 useEffect 的依赖,其返回值会被深克隆,引用地址会发生改变 | Function(fields: string[]) | |
| getFieldError | 获取一个表单控件的错误状态 | Function(field: string) | |
| getFieldsError | 获取一组表单控件的错误状态,如果不设置 fields 的话,会获取所有的控件的错误状态。 | Function(fields?: string[]) | |
| scrollToField | 滚动到指定表单字段位置。ScrollIntoViewOptions | Function(field: string, options?: ScrollIntoViewOptions) | |
| getTouchedFields | 获取被操作过的字段 | () => string[] | |
| resetFields | 重置表单控件的值变为初始值 | Function(field?: string[]) | |
| clearFields | 清除表单控件的值以及校验状态。等同于 form.setFields({ [field]: { value: undefined; error: null, warning: null } }) | Function(field?: string[]) | 2.29.0 |
validate 用法
this.form.validate((errors, values) => {
console.log(errors, values);
});
// 或者
this.form.validate().then((values) => {
console.log(values);
}).catch((error) => {
console.log(error.name);
console.log(error.message);
console.log(error.errors);
});
// 或者 (注意要在 async 方法中使用)
try {
const values = await this.form.validate();
} catch (error) {
console.log(error.name);
console.log(error.message);
console.log(error.errors);
}
setFields 用法
this.form.setFields({
name: {
value: 'pjy',
error: {
message: 'Yes, I know!'
},
warning: 'warning...'
}
});
常见问题
为什么无法调用 Modal 里嵌套的 Form 的方法?
例如以下使用方式:
// ...
const [form] = Form.useForm();
useEffect(() => {
form.setFieldsValue({})
}, [])
return <div>
<Modal visible={visible}>
<Form form={form}>
{/** ... */}
</Form>
</Modal>
</div>
这是因为在调用 form 的方法时,Modal 还未挂载,Form 还未创建。 可以设置 Modal 的 mountOnEnter=false 。
Form.Control
非常非常极其不建议直接使用Form.Control. 在1.15.0及以上版本,组件库对 Form.Item 进行了较多功能扩展,简化了受控表单的写法,不再推荐直接使用Form.Control 组件(当然1.x兼容了此写法)。但升级到2.0后,Form.Control作为Form的一个内部组件,为了功能升级或bugfix,可能会进行一些改动,较少考虑到外部直接使用的场景,所以非常建议使用Form.Item代替Form.Control。
如何设置表单控件的默认值?(为什么给控件例如 Input ,直接设置 defaultValue 不生效)
在被 FormItem包裹,并且 Form.Item 设置了 field 属性的控件上,不要再设置 defaultValue和 value 属性。 可以在 Form.Item 上通过 initialValue 来设置默认值。
Modal 里嵌套的 Form 时,无法调用 Form 的方法。
Modal 默认在打开时挂载,所以页面加载完成时 Modal 还未挂载,Form 还未创建。
可以设置 Modal 的 mountOnEnter=false 。
传了 initialValue 但不生效。
initialValue 解释为初始值,只会在表单挂载时生效,如果表单数据是异步加载的请用 setFieldValue / setFieldsValue / setFields 更新表单值。
Form.Item 未收集到所包裹表单控件的值 / 未触发校验?
- Form.Item 设置了 field 属性
- Form.Item 直接包裹在表单控件外,并且表单控件是 FormItem 的唯一子节点
- Form.Item 的默认 triggerPropName 是 value,表示会给子控件注入 value 属性。如果表单控件的受控属性是
checked(如 switch, checkbox, radio),fileList(如 Upload)或者其他,那么需要根据实际情况设置下Form.Item的 triggerPropName={表单控件的受控属性} - 类似 triggerPropName,默认
FormItem监听子组件的onChange回调来收集控件值以及触发校验逻辑。如果子组件的回调不是onChange,记得设置下FormItem的trigger={表单控件的事件回调} - Form.Item 所包裹的控件是挂载状态(即未被销毁)
某个表单元素填了值但已经被销毁,我如何获取这个值?
**getFields**方法会获取所有在 Form 中注册过的字段的值,包括被创建后销毁的表单项。
校验节流 或 异步校验?
在 rules 中自定义 validator 方法,并返回一个 Promise 即可实现表单的异步校验。
节流如果用 lodash.debounce 不生效,建议使用 debounce.promise ,它返回的是一个 promise。
官网示例
Form 中对 Checkbox/Switch/Upload 设置值不生效
在 Checkbox/Switch 对应的 Form.Item 上设置 triggerPropName=checked
在 Upload 对应的 Form.Item 上设置 triggerPropName=fileList
其他组件类似
Form 内 Input 出现浅蓝色背景色(Chrome 浏览器)?
蓝色背景色是 Chrome 浏览器的自动填充样式,关闭 form 的自动填充即可。
如何关闭自动填充?
Form 组件的原生属性,设置 autoComplete=off 即可。
点击 label 会触发对应表单控件的行为改变?如:switch 切换,checkbox 选中, input 聚焦
https://www.runoob.com/tags/tag-label.html 标签定义及使用说明 label 标签为 input 元素定义标注(标记)。 label 元素不会向用户呈现任何特殊效果。不过,它为鼠标用户改进了可用性。如果您在 label 元素内点击文本,就会触发此控件。就是说,当用户选择该标签时,浏览器就会自动将焦点转到和标签相关的表单控件上。 label 标签的 for 属性应当与相关元素的 id 属性相同。 如果实在不想要这个效果,给表单控件随便设置个 id 属性,不要和 label 的 for 属性一致即可。如
<Switch id="aaaaa" />
对错误信息的展示进行高级定制
https://codepen.io/yinkaihui/pen/GRwRQao?editors=1011
DatePicker.WeekPicker 传值解析不了
DatePicker.QuarterPicker 同理 给
Form.Item加上getValueFromEvent={(v, dv) => dv}。