根据antd的官方文档,validateFields接收一个NamePath类型的数组作为参数,格式如下:
(nameList?: NamePath[]) => Promise
举个简单的例子:
<Form form={form}>
<Form.Item name='a'></Form.Item>
<Form.Item name='b'></Form.Item>
<Form.Item name='c'></Form.Item>
</Form>
const [form] = Form.useForm();
// 校验全部,由于nameList是可选参数,不传的情况下就是校验改表单下的全部
form.validateFields().then(values => {})
//校验a、b
form.validateFields(['a','b']).then(values => {})
Form.List的校验问题
<Form form={form}>
<Form.Item name='a'></Form.Item>
<Form.List name="list">
{({name}, { add, remove }, { errors }) => (
<>
<Form.Item name='b'></Form.Item>
<Form.Item name='c'></Form.Item>
</>
)}
</Form.List>
</Form>
const [form] = Form.useForm();
类似上面这段伪代码,如果想要校验Form.List的第一个元素(['list', 0]),并且获取到对应的值,那又要如何实现呢?
form.validateFields(['list', 0]).then(values => {})
form.validateFields([['list', 0]]).then(values => {})
上面这两种写法都是有问题的,安装官方文档来理解,看起来第二种应该是可以的,但是其实这种写法只是校验到Form.List的rules,不会继续往下遍历了。难道需要把全部都列举出来吗,类似:
form.validateFields([['list', 0, 'a'], ['list', 0, 'b']]).then(values => {})
未免有点麻烦了,特别是当字段很多或者有更深层次嵌套的时候。从官方文档也没能找到更好的方式,但是本文接下来会提供一个方式,当然,这里要强调一下,目前基于antd(4.23.0),rc-field-form(1.27.2)来进行讲解的。
从antd的form表单源码可以看到,其实antd中的form是基于rc-field-form进行包装的,其中也包括useForm:
import { useForm as useRcForm } from 'rc-field-form';
然后我们再来看一下rc-field-form的代码,可以看到其中关于validateFields这个api,其实除了antd官方文档中的第一个参数nameList,还有第二个参数options:
private validateFields: InternalValidateFields = (
nameList?: NamePath[],
options?: ValidateOptions,
) => {
// xxx
}
export interface ValidateOptions {
triggerName?: string;
validateMessages?: ValidateMessages;
/**
* Recursive validate. It will validate all the name path that contains the provided one.
* e.g. ['a'] will validate ['a'] , ['a', 'b'] and ['a', 1].
*/
recursive?: boolean;
}
从这个上面看起来,一年11个月前加的recursive,就是我们解决问题的关键。似乎我们可以这样写:
form.validateFields([['list', 0]], {recursive: true}).then(values => {})
但是,运行的效果并不理想,没有我们想象中的深入递归校验的效果,换一种写法呢:
form.validateFields(['list', 0], {recursive: true}).then(values => {})
校验效果有了,但是获取到的values有点奇怪。难道就没有完整的解决方案了吗,有的,你可以这样实现:
form.validateFields(['list', 0], {recursive: true}).then(() => {
const values = form.getFieldsValue([['list', 0]]);
console.log(values);
})
之所以这样,是因为validateFields的实现逻辑里面,recursive的时候:
if (options?.recursive && provideNameList) {
const namePath = field.getNamePath();
if (
// nameList[i] === undefined 说明是以 nameList 开头的
// ['name'] -> ['name','list']
namePath.every((nameUnit, i) => nameList[i] === nameUnit || nameList[i] === undefined)
) {
namePathList.push(namePath);
}
}
所以,当recursive为true的时候,只支持['list', 0],如果是[['list', 0]]这种,那么是没办法进if的。
至于validateFields的values获取,是基于getFieldsValue实现的,getFieldsValue是支持并需要用到[['list', 0]]这种的,伪代码如下:
const returnPromise: Promise<Store | ValidateErrorEntity | string[]> = summaryPromise
.then((): Promise<Store | string[]> => {
if (this.lastValidatePromise === summaryPromise) {
return Promise.resolve(this.getFieldsValue(namePathList));
}
return Promise.reject<string[]>([]);
})
return returnPromise as Promise<Store>;
到了这一步,就把上面解决方案的实现逻辑给理清楚了!