Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
TinkGu opened this issue Mar 3, 2017 · 43 comments
Labels

Comments

@TinkGu
Copy link

TinkGu commented Mar 3, 2017

感谢提供 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 最新
@benjycui
Copy link
Contributor

benjycui commented Mar 3, 2017

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?

@afc163
Copy link
Member

afc163 commented Mar 7, 2017

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

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

@afc163
Copy link
Member

afc163 commented Mar 7, 2017

文档已更新。

@LukerSpringtree
Copy link

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

@neekey
Copy link
Contributor

neekey commented Sep 25, 2017

这个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
Copy link
Member

afc163 commented Sep 25, 2017

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

@xiaoxiangmoe
Copy link

xiaoxiangmoe commented Apr 26, 2018

@neekey 这种奇怪的接口是为了实现:

  • 异步的validation
  • 支持不同的错误返回不同的message

有没有更好的方式啊?

@aliasese
Copy link

aliasese commented Sep 28, 2018

<Row>
                <Col span={24} key={"method"}>
                    <FormItem {...formItemMethodLayout} label="请求方法">
                        {getFieldDecorator("method", {
                            initialValue: this.state.data.method,
                            validateTrigger: "onChange",
                            rules: [{
                                required: true,
                                message: `请选择请求方法`
                            }, {
                                //validator: this.state.checkUnique ? this.checkConfirmMethod.bind(this) : (rule, value, callback) => {callback();},
                                validator: (这如何判断当前组件是否开始校验了) 
 this.checkConfirmMethod.bind(this) : (如果不是当前组件开始校验,就调用其他函数),
                            }]
                        })(
                            <Select placeholder={"请选择请求方法"} onChange={::this.handleChange.bind(this)}>
                                {this.state.apiRequestMethodSelect}
                            </Select>
                        )}
                    </FormItem>
                </Col>
            </Row

@lcoder
Copy link

lcoder commented Oct 27, 2018

每次碰到函数的校验,都会来这里看一次,真的不好记

@jeffrey-fu
Copy link

jeffrey-fu commented Nov 4, 2018

validator 函数

startNumValidator = (rule, value, callback) => {
    const { exportDataCount } = this.props;
    const reg = /^[1-9][0-9]*$/;
    let errors = [];
    if (!reg.test(value) || value > exportDataCount) {
      errors.push(new Error('illegal value'));
    }
    callback(errors);
  }

不是统一callback errors么,也挺合理的啊感觉
不知道新版的antd开始区分异步和同步校验了没? validator(同步) asyncValidator(异步)

@afc163 afc163 added the ❓FAQ issues would be discussed a lot label Nov 4, 2018
@chiove
Copy link

chiove commented Nov 26, 2018

在请求后,后台返回的回调里面怎么调用这个消息提示

@zerpor
Copy link

zerpor commented Mar 21, 2019

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

@aliasese
Copy link

在对应的FormItem组件下显示的逻辑是怎么书写的?

@lcoder
Copy link

lcoder commented Mar 22, 2019

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

有人提了,yiminghe说,反对。callback 更直接。都9102年了,还用callback,坐等真香

@zerpor
Copy link

zerpor commented Mar 22, 2019

在对应的FormItem组件下显示的逻辑是怎么书写的?

我把自定义校验的方案改了,在onFieldsChange()中判断,不符合自定义的规则就直接改errors,然后就会在控件中显示error信息
onFieldsChange(props, changedFields) { const { dispatch, detail } = props; const finalChangedFields = { ...changedFields }; // 校验时间大小是否符合规则 if ( _.has(changedFields, 'currentDate') && detail?.targetDate && compareCurrentTimeEarlierThanTargetTime( finalChangedFields.currentDate, detail?.targetDate ) ) { finalChangedFields.currentDate.errors = [ { message: 'current Date should be latter than target Date', field: 'currentDate', }, ]; } dispatch({ type: 'controllerModel/saveDetail', payload: { changedFields: finalChangedFields, data: detail, }, }); }

@jiufengdadi
Copy link

校验通过后下面的提示不消失是怎么回事

@mengdianliang
Copy link

问一个问题,这个validator能不能在页面加载的时候,不做校验?

@yifei-zhan
Copy link

yifei-zhan commented May 18, 2019

@neekey
must call a callback feature is a big drawback in async situation:

<Form>
          <Form.Item ...>
            {getFieldDecorator('name', {validator: (rule, value, callback) => setTimeout(callback(), 1000)})(
                <Input placeholder="name" />
            )}
          </Form.Item>
....

and before the setTimeout is fired, the whole component is unmounted. Then the timeout is fired, its callback is fired and

component will use setState(I guess) which will result in the following error:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

Sadly the callback is not cancellable ! No way to avoid this error!

@gxvv
Copy link
Contributor

gxvv commented Jun 11, 2019

@jiufengdadi
I encountered this problem.
My solution is pass undefined into the callback. The validator will pass when the first parameter of the callback is an empty array. And it set a default value to the parameter, so undefined is also work.

the callback definition: L146

@k983551019
Copy link
Contributor

为什么我自定义验证通过了 还出现async-validator: [""] ,

@LastStranger
Copy link

如果有两个校验的话,第一个会失效,为什么?

 rules: [
                {
                  required: true,
                  message: '请输入手机号',
                },
                {
                  validator: (rule, value, callback) => {
                    try {
                      if (this.props.form.getFieldValue('prefix') == 86 && value) {
                        const reg = /^1\d{10}$/;
                        if (!reg.test(value)) {
                          throw new Error('Something wrong!');
                        }
                      }
                    } catch (err) {
                      callback(err);
                    }
                  },
                  message: '请输入有效手机号',
                },
              ],

@yoyo837
Copy link
Contributor

yoyo837 commented Sep 6, 2019

 rules: [
                {
                  required: true,
                  message: '请输入手机号',
                },
                {
                  validator: (rule, value, callback) => {
                    try {
                      if (this.props.form.getFieldValue('prefix') == 86 && value) {
                        const reg = /^1\d{10}$/;
                        if (!reg.test(value)) {
                          throw new Error('Something wrong!');
                        }
                      }
                    } catch (err) {
                      callback(err);
                       return // +
                    }
                     callback() // +
                  },
                  message: '请输入有效手机号',
                },
              ],

@weilun0510
Copy link

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

你解决了吗,遇到同个情况

@Composur
Copy link

Composur commented Nov 4, 2019

 rules: [
                {
                  required: true,
                  message: '请输入手机号',
                },
                {
                  validator: (rule, value, callback) => {
                    try {
                      if (this.props.form.getFieldValue('prefix') == 86 && value) {
                        const reg = /^1\d{10}$/;
                        if (!reg.test(value)) {
                          throw new Error('Something wrong!');
                        }
                      }
                    } catch (err) {
                      callback(err);
                       return // +
                    }
                     callback() // +
                  },
                  message: '请输入有效手机号',
                },
              ],

message提示不会重复吗?'请输入手机号 请输入有效手机号' 类似这样

@k983551019
Copy link
Contributor

 rules: [
                {
                  required: true,
                  message: '请输入手机号',
                },
                {
                  validator: (rule, value, callback) => {
                    try {
                      if (this.props.form.getFieldValue('prefix') == 86 && value) {
                        const reg = /^1\d{10}$/;
                        if (!reg.test(value)) {
                          throw new Error('Something wrong!');
                        }
                      }
                    } catch (err) {
                      callback(err);
                       return // +
                    }
                     callback() // +
                  },
                  message: '请输入有效手机号',
                },
              ],

message提示不会重复吗?'请输入手机号 请输入有效手机号' 类似这样

不会

@jasonzhangdong
Copy link

jasonzhangdong commented Nov 14, 2019

image

做如下操纵
image

为什么红色的校验还不消失呢???都满足规格了 这种相互影响的校验怎么做???

@yoyo837
Copy link
Contributor

yoyo837 commented Nov 14, 2019

rule的validator 成功失败与否都要调用callback,区别是传不传参数

@Minglu-Huo
Copy link

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

你解决了吗,遇到同个情况

我也遇到了这个问题, 如果initialvalue为空, 在正常输入内容后通过onblur时间可以正常触发自定义校验器并有错误的情况下会进行提示, 但一旦初始initialvalue是有值的情况下, 在validatefields 进行所有表单组件统一校验时, err打印出来是null, 过了一会后 又会在控制台弹出校验失败, 自定义验证器是异步action

@wenk001
Copy link

wenk001 commented Nov 21, 2019 via email

@ChinaHDJ1
Copy link

太坑了

@blueskeys
Copy link

校验函数里面如果出现异常,没有任何提示。
坑了半天。。。

@kongling94
Copy link

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

@afc163
Copy link
Member

afc163 commented Feb 21, 2020

@kongling94 ?

@scomparsa
Copy link

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

@runningvip
Copy link

<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
Copy link

<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
Copy link

<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
Copy link

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

@RainLucky
Copy link

RainLucky commented Apr 4, 2021

我遇到的问题是我使用了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
Copy link



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
Copy link

keminu commented May 24, 2022

2022 年了,还callback嘛?

@LSunnyCN
Copy link

LSunnyCN commented Feb 1, 2023

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

<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
Copy link

BoBoooooo commented Feb 15, 2023

这个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的方法有计划支持么。。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests