Skip to content

why we always need callback() in validator function at Form component? 为什么表单验证的 rules 中使用 validator 时,其方法体总是需要调用 callback() #5155

@TinkGu

Description

@TinkGu

感谢提供 Form 等一系列组件。

问题描述

由于要在提交前,需要对表单内的各个 field 进行再次校验,使用了 validateFieldsAndScroll。但发现并未生效。

后来经调试,发现如果对该函数传入一组指定的 field,是可以生效的。但只要这组 fileds 中,包含了任意一个在校验规则中指定使用 validator 辅助校验的,则整个 validateFields 方法都不生效。

最后,才发现,是因为,我一开始并没有在对应的 validator 回调中,总是返回 callback() (觉得很多余,而且一开始不写也可以对单个 input 做正常校验)。加上以后就 ok 了。

疑惑

请问,这属于 bug 吗? 因为文档中并没有指出,validator 中必须要返回 callback()
如果确实是必须要返回 callback ,建议要在文档中提醒开发者注意,或者默认可以缺省。

代码片段

handleSubmit = (e) => {
        e.preventDefault()

        const { onSubmit } = this.props
        const { validateFieldsAndScroll, getFieldsValue, resetFields } = this.props.form

        validateFieldsAndScroll((err, values) => {
              if (!err) {
                  onSubmit(getFieldsValue())
              }
        })
}
handleConfirmPassword = (rule, value, callback) => {
        const { getFieldValue } = this.props.form
        if (value && value !== getFieldValue('newPassword')) {
            callback('两次输入不一致!')
        }

        // Note: 必须总是返回一个 callback,否则 validateFieldsAndScroll 无法响应
        callback()
 }

render()

<FormItem
    {...formItemLayout}
    label="确认密码"
>
{
    getFieldDecorator('confirmPassword', {
           rules: [{
                  required: true,
                  message: '请再次输入以确认新密码',
            }, {
                  validator: this.handleConfirmPassword
            }],
    })(<Input type="password" />)
}
</FormItem>

Environment

  • antd version: 最新
  • OS and its version: OS X
  • Browser and its version: chrome 最新

Activity

benjycui

benjycui commented on Mar 3, 2017

@benjycui
Contributor

It's by designed: https://github.com/yiminghe/async-validator/

BTW, how does async-validator know an asynchronous procedure is end, if you don't use callback to notify it?

reopened this on Mar 7, 2017
afc163

afc163 commented on Mar 7, 2017

@afc163
Member

被人吐槽了 https://www.zhihu.com/question/33629737/answer/150154145

可以加一个文档说明,我们做开源的原则之一是兵来将挡水来土掩。

afc163

afc163 commented on Mar 7, 2017

@afc163
Member

文档已更新。

LukerSpringtree

LukerSpringtree commented on Jul 21, 2017

@LukerSpringtree

你好是否 这种用法 只能用 class 的写法?
能否用 纯函数的 写法 写 React Component .如果有,有没有案例啊?

neekey

neekey commented on Sep 25, 2017

@neekey
Contributor

这个API设计真的是很不优雅,感受下我只是想做一下某个checkbox是否选中的检查:

rules: [
            {
              message: 'You need to agree to our terms to sign up.',
              validator: (rule, value, cb) => (value === true ? cb() : cb(true)),
            },
          ],

并且给到callback的第一个参数是错误信息,和message也是冗余(虽然似乎只要返回任何东西都会使用message了,应该是message 覆盖了错误信息)。

大部分使用者预期的使用方式应该是直接返回true或者false来决定对错:

rules: [
            {
              message: 'You need to agree to our terms to sign up.',
              validator: (rule, value, cb) => value === true,
            },
          ],

可用,但是还是建议优化这个API,即使用的是 https://github.com/yiminghe/async-validator/,但是ANT这边完成全可以在调用上封装一下

afc163

afc163 commented on Sep 25, 2017

@afc163
Member

@neekey 可以去给 async-validator 提个兼容的 PR

30 remaining items

kongling94

kongling94 commented on Feb 21, 2020

@kongling94

我在想这个AntdV是不维护了么

afc163

afc163 commented on Feb 21, 2020

@afc163
Member
mark-lauq

mark-lauq commented on Mar 3, 2020

@mark-lauq

同样遇到这个问题,没下文了?

runningvip

runningvip commented on May 6, 2020

@runningvip

<a-input id="code" placeholder="请输入用户ID"v-decorator="[ 'code', {rules: [{ required: true, message: '请输入用户ID' }, { validator: handleUsername }], validateTrigger: 'change'} ]" > </a-input>
handleUsername(rule, value, callback) { value = value.trim() if (value.length == 0) { this.username = ''; callback(); } else { fetchUsername({ code: value }) .then((rest) => { if (rest.code == 200) { this.username = rest.data; //callback(); } else { this.username = ''; callback(new Error(rest.msg)) return; } }).finally(() => { callback() }) } },
<a-form :label-col="labelCol" :wrapper-col="wrapperCol" :form="form" @submit="handleSubmit"> <a-button type="primary" htmlType="submit">确定</a-button> </a-form>
点确定没有任何反应 不进行任何提示,在console 里面输入async-validator:["code is required"]
根本没有进validateFields
去掉 validator 和 validateTrigger 点确定就会正常提示。
这是什么问题?

Nicole1991

Nicole1991 commented on Jul 27, 2020

@Nicole1991

<a-input id="code" placeholder="请输入用户ID"v-decorator="[ 'code', {rules: [{ required: true, message: '请输入用户ID' }, { validator: handleUsername }], validateTrigger: 'change'} ]" > </a-input>
handleUsername(rule, value, callback) { value = value.trim() if (value.length == 0) { this.username = ''; callback(); } else { fetchUsername({ code: value }) .then((rest) => { if (rest.code == 200) { this.username = rest.data; //callback(); } else { this.username = ''; callback(new Error(rest.msg)) return; } }).finally(() => { callback() }) } },
<a-form :label-col="labelCol" :wrapper-col="wrapperCol" :form="form" @submit="handleSubmit"> <a-button type="primary" htmlType="submit">确定</a-button> </a-form>
点确定没有任何反应 不进行任何提示,在console 里面输入async-validator:["code is required"]
根本没有进validateFields
去掉 validator 和 validateTrigger 点确定就会正常提示。
这是什么问题?

请问你这个问题解决了吗?同样碰到了这个问题

weilun0510

weilun0510 commented on Jul 31, 2020

@weilun0510

<a-input id="code" placeholder="请输入用户ID"v-decorator="[ 'code', {rules: [{ required: true, message: '请输入用户ID' }, { validator: handleUsername }], validateTrigger: 'change'} ]" > </a-input>
handleUsername(rule, value, callback) { value = value.trim() if (value.length == 0) { this.username = ''; callback(); } else { fetchUsername({ code: value }) .then((rest) => { if (rest.code == 200) { this.username = rest.data; //callback(); } else { this.username = ''; callback(new Error(rest.msg)) return; } }).finally(() => { callback() }) } },
<a-form :label-col="labelCol" :wrapper-col="wrapperCol" :form="form" @submit="handleSubmit"> <a-button type="primary" htmlType="submit">确定</a-button> </a-form>
点确定没有任何反应 不进行任何提示,在console 里面输入async-validator:["code is required"]
根本没有进validateFields
去掉 validator 和 validateTrigger 点确定就会正常提示。
这是什么问题?

请问你这个问题解决了吗?同样碰到了这个问题

// 自定义密码校验
  handleConfirmPassword = (rule, value, callback) => {
    const { getFieldValue } = this.props.form;
    if (value && value !== getFieldValue('newPwd')) {
      callback('两次输入不一致!');
    }

    // Note: 必须总是返回一个 callback,否则 validateFieldsAndScroll 无法响应
    callback();
  };
我是这么解决的
angle12138

angle12138 commented on Aug 15, 2020

@angle12138

validatorFun(rule, value, callback) {
const psw = this.form.getFieldValue('newPwd')
if (value && value !== psw) {
rule.message = '两次密码不一致'
callback(rule)
}
callback()
}

RainLucky

RainLucky commented on Apr 4, 2021

@RainLucky

我遇到的问题是我使用了a-radio-group的,然后我只对其进行空值校验,因为有需求要在选择两个条件后发起请求,所以又对radio-group做了@change监听,两个单选组必须都选择了才发起请求,此时用@change来validate这两个条件值是否为空,一开始为空时,正常提示,后面两个值都不为空时不走validate().then()而是走的catch(error)为什么?而且console.log(error)打印出来的errorFields是空的,value显示两个校验字段都有值了,百思不得解。
image

<a-form
              :label-col="labelCol"
              :wrapper-col="wrapperCol"
              :scroll-to-first-error="true"
              name="form"
            >
              <a-form-item v-bind="validateInfos.radio1" label="条件1">
                <a-radio-group
                  name="radioGroup"
                  v-model:value=“radio1"
                  @change="onRadioChange()"
                >
               
                <a-radio value="001">ceshi1</a-radio>
                <a-radio value="002">ceshi2</a-radio>
                <a-radio value="003">ceshi3</a-radio>

                </a-radio-group>
              </a-form-item>

              <a-form-item v-bind="validateInfos.radio2" label="条件2">
                <a-radio-group
                  name="radioGroup2"
                  v-model:value="radio2"
                  @change="onRadioChange()"
                >
                  <a-radio value="1">1</a-radio>
                  <a-radio value="2">2</a-radio>
                
                </a-radio-group>
              </a-form-item>
            
              <a-form-item label="查询结果">
                <a-row>
                  <a-col span="14">
                    <span class="none_text">暂无信息</span>
                    <span class="enough">充足</span>
                    <span class="full">已满</span>
                  </a-col>
                  <a-col span="10" align="right" v-if="isAllowSignBtn">
                    <a-button
                      class="submit"
                      type="primary"
                      @click="onSubmit"
                      >提交</a-button
                    >
                  </a-col>
                </a-row>
              </a-form-item>
              <!-- 图形验证码 -->
              <a-form-item
                label="图形验证码"
                v-bind="validateInfos.ImgCode"
              >
                <a-row>
                  <a-col :lg="8" :md="10" :sm="12"
                    ><a-input
                      v-model:value="ImgCode"
                      placeholder="请输入图形验证码"
                  /></a-col>
                  <a-col :lg="4" :md="5" :sm="6" style="top: -1px"
                    ><img
                      class="img_code"
                      v-bind:src="ImgSrc"
                      @click="onRefreshImg()"
                  /></a-col>
                </a-row>
              </a-form-item>
              <!-- /图形验证码 -->
            </a-form>
            
            
   <script>
     // 校验规则
    const rulesRef = reactive({
      radio1: [
        {
          required: true,
          validator:async (rule, value) => {
    if (value === "") {
      return Promise.reject("条件1不能为空");
    } 
    else  {
      return Promise.resolve();
    }
  },
          trigger: "change",
          whitespace: true,
        },
      ],
      subId: [
        {
          required: true,
          validator:async (rule, value) => {
    if (value === "") {
      return Promise.reject("条件2不能为空");
    } 
    else  {
      return Promise.resolve();
    }
  },
          trigger: "change",
          whitespace: true,
        },
      ],
      ImgCode: [
        {
          required: true,
          validator: validateRule.validateImgCode,
          trigger: "change",
        },
      ]

    });

    const onSchoolIdChange = () => {
      validate(['radio1', 'radio2']).then(() => {
        console.log('校验通过,是否发起了查询');
     
      }).catch((error) =>{
      //控制台输出此信息,为什么??
        console.log('onRadioChange error',error);
      })
    }
    
   const onSubmit = () => {
   console.log('点击了提交')
    validate().then(() => {
        console.log('radio1,radio2,ImgCode校验通过')
    }).catch((error) => {
        console.log('error',error);
    })
   }
   <script>
woshidashengtadie

woshidashengtadie commented on Apr 7, 2022

@woshidashengtadie



const money = (rule: any, value: any, callback:any) => { console.log(rule, value);callback('请输入合法金额数字') } warning.js:6 Warning: callback is deprecated. Please return a promise instead.

keminu

keminu commented on May 24, 2022

@keminu

2022 年了,还callback嘛?

LSunnyCN

LSunnyCN commented on Feb 1, 2023

@LSunnyCN

我在提交前进行所有字段校验时报错,定位到是因为一个自定义校验,但不知道为什么会报错。
自定义校验

<a-form-item label="联系电话">
          <a-input
            v-else
            v-decorator="['phoneNumber', {rules: [{required: true, min: 1, message: '联系电话不能为空!'}, {pattern: /^(1[3|4|5|6|7|8|9])[0-9]{9}$/, message: '手机号码格式错误!'}, {validator: phoneCheck}]}]"
          />
        </a-form-item>
async phoneCheck (rule, value, callbackFn) {
      const that = this
      await validatePhoneNumber(value).then(res => {
        if (that.userInfoParams.phoneNumber !== value && res.data) {
          callbackFn('该手机号已被使用,请修改后重试!')
          return false
        }
      })
      await callbackFn()
    }

提交后的校验
form.validateFields((errors, values) => {})
error信息

{
    "phoneNumber": {
        "expired": true,
        "errors": [
            {
                "message": "phoneNumber need to revalidate",
                "field": "phoneNumber"
            }
        ]
    }
}
BoBoooooo

BoBoooooo commented on Feb 15, 2023

@BoBoooooo

这个API设计真的是很不优雅,感受下我只是想做一下某个checkbox是否选中的检查:

rules: [
            {
              message: 'You need to agree to our terms to sign up.',
              validator: (rule, value, cb) => (value === true ? cb() : cb(true)),
            },
          ],

并且给到callback的第一个参数是错误信息,和message也是冗余(虽然似乎只要返回任何东西都会使用message了,应该是message 覆盖了错误信息)。

大部分使用者预期的使用方式应该是直接返回true或者false来决定对错:

rules: [
            {
              message: 'You need to agree to our terms to sign up.',
              validator: (rule, value, cb) => value === true,
            },
          ],

可用,但是还是建议优化这个API,即使用的是 https://github.com/yiminghe/async-validator 但是ANT这边完成全可以在调用上封装一下

2023年了。。请问这个 validator: ()=> boolean的方法有计划支持么。。

liebodyuebai

liebodyuebai commented on Oct 30, 2024

@liebodyuebai

我在提交前进行所有字段校验时报错,定位到是因为一个自定义校验,但不知道为什么会报错。 自定义校验

<a-form-item label="联系电话">
          <a-input
            v-else
            v-decorator="['phoneNumber', {rules: [{required: true, min: 1, message: '联系电话不能为空!'}, {pattern: /^(1[3|4|5|6|7|8|9])[0-9]{9}$/, message: '手机号码格式错误!'}, {validator: phoneCheck}]}]"
          />
        </a-form-item>
async phoneCheck (rule, value, callbackFn) {
      const that = this
      await validatePhoneNumber(value).then(res => {
        if (that.userInfoParams.phoneNumber !== value && res.data) {
          callbackFn('该手机号已被使用,请修改后重试!')
          return false
        }
      })
      await callbackFn()
    }

提交后的校验 form.validateFields((errors, values) => {}) error信息

{
    "phoneNumber": {
        "expired": true,
        "errors": [
            {
                "message": "phoneNumber need to revalidate",
                "field": "phoneNumber"
            }
        ]
    }
}

解决了么

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @neekey@afc163@blueskeys@benjycui@mark-lauq

        Issue actions

          why we always need `callback()` in validator function at Form component? 为什么表单验证的 rules 中使用 validator 时,其方法体总是需要调用 callback() · Issue #5155 · ant-design/ant-design