淘汰赛验证异步验证:这是一个错误还是我做错了什么?(Knockout Validation asyn

2019-08-01 03:51发布

我真的很喜欢如何埃里克·巴纳德的淘汰赛验证LIB与观测相集成,允许分组,及提供自定义验证可插拔(包括即时验证)。 有一对夫妇的地方,它可以更灵活UX /友好的,但总体来说是合理的证据充分的...... 除了,国际海事组织,当涉及到异步验证 。

今天我跟这个搏斗了几个小时做一个搜索和之前降落在此 。 我觉得我有同样的问题/疑问原作者,但同意,目前还不清楚到底是什么DUXA问了。 我想带来的问题更多的关注,所以我也问在这里。

function MyViewModel() {
    var self = this;
    self.nestedModel1.prop1 = ko.observable().extend({
        required: { message: 'Model1 Prop1 is required.' },
        maxLength: {
            params: 140,
            message: '{0} characters max please.'
        }
    });
    self.nestedModel2.prop2 = ko.observable().extend({
        required: { message: 'Model2 Prop2 is required' },
        validation: {
            async: true,
            validator: function(val, opts, callback) {
                $.ajax({                                  // BREAKPOINT #1
                    url: '/validate-remote',
                    type: 'POST',
                    data: { ...some data... }
                })
                .success(function(response) {
                    if (response == true) callback(true); // BREAKPOINT #2
                    else callback(false);
                });
            },
            message: 'Sorry, server says no :('
        }
    });
}

ko.validation.group(self.nestedModel1);
ko.validation.group(self.nestedModel2);

一对夫妇约上面的代码的注释:有2个独立的验证组,一组用于每个嵌套模型。 嵌套模型#1不具有异步验证器,和嵌套模型#2具有两个同步(必需)和一个异步。 异步调用服务器呼叫来验证输入。 当服务器响应时, callback参数是用来告诉ko.validation用户的输入是否是好还是坏。 如果你把上线断点指示和触发验证使用已知的无效值,你结束了一个无限循环了AJAX success功能使validator功能再次调用。 我破解打开ko.validation源代码,看看发生了什么事情。

ko.validation.validateObservable = function(observable) {
    // set up variables & check for conditions (omitted for brevity)

    // loop over validators attached to the observable
    for (; i < len; i++) {
        if (rule['async'] || ctx['async']) {
            //run async validation
            validateAsync();
        } else {
            //run normal sync validation
            if (!validateSync(observable, rule, ctx)) {
                return false; //break out of the loop
            }
        }
    }

    //finally if we got this far, make the observable valid again!
    observable.error = null;
    observable.__valid__(true);
    return true;
}

该功能是在附接至所述用户输入可观察的预订链,使得当其值改变时,新的值将被验证。 该算法遍历附接到输入每个校验和执行取决于验证是否是异步或不独立的功能。 如果同步验证失败,循环被打破,整个validateObservable函数退出。 如果所有的同步验证通过,最后3行执行,实质上是告诉ko.validation此输入是有效的。 该__valid__库中的函数如下所示:

//the true holder of whether the observable is valid or not
observable.__valid__ = ko.observable(true);

有两件事情,从这个带走: __valid__是可观察到的,它被设置为truevalidateAsync函数退出。 现在,让我们来看看validateAsync

function validateAsync(observable, rule, ctx) {
    observable.isValidating(true);

    var callBack = function (valObj) {
        var isValid = false,
            msg = '';

        if (!observable.__valid__()) {
            // omitted for brevity, __valid__ is true in this scneario
        }

        //we were handed back a complex object
        if (valObj['message']) {
            isValid = valObj.isValid;
            msg = valObj.message;
        } else {
            isValid = valObj;
        }

        if (!isValid) {
            //not valid, so format the error message...
            observable.error = ko.validation.formatMessage(...);
            observable.__valid__(isValid);
        }

        // tell it that we're done
        observable.isValidating(false);
    };

    //fire the validator and hand it the callback
    rule.validator(observable(), ctx.params || true, callBack);
}

需要注意的是之前仅此函数的第一个和最后一个行执行的是很重要的ko.validation.validateObservable设置__valid__可观察到真实和退出。 该callBack函数就是被作为第三个参数的异步传递validator中声明功能MyViewModel 。 出现这种情况然而之前,一个isValidating观察到的用户被调用,以通知异步验证已经开始。 当服务器调用完成后,调用回调函数(在这种情况下只是路过true或false)。

现在,这里就是为什么在断点MyViewModel当服务器端验证失败,也引起无限乒乓循环:在callBack上述功能,请注意如何__valid__在验证失败时可观察到的设置为false。 这里是发生了什么:

  1. 无效的用户输入改变nestedModel2.prop2观察到。
  2. ko.validation.validateObservable通过这种变化的通知订阅。
  3. validateAsync函数被调用。
  4. 自定义验证异步调用,它提交一个异步$.ajax调用服务器并退出。
  5. ko.validation.validateObservable 设置__valid__可观察到true和退出
  6. 服务器返回一个无效的响应,和callBack(false)被执行。
  7. callBack函数设置__valid__false
  8. 所述ko.validation.validateObservable被通知改变到的__valid__观察到的( callBack改变了它从truefalse )这基本上重复上面的步骤2。
  9. 步骤3,4和5的上方被重复。
  10. 由于可观察到的值没有改变,则服务器返回另一个无效的响应,触发步骤6,7,8,和9以上。
  11. 我们有自己乒乓球比赛。

因此,它似乎是一个问题是, ko.validation.validateObservable订阅处理器是听不只是用户的输入值的变化,也改变了其嵌套__valid__观察到。 这是一个错误,还是我做错了什么?

次要问题

您可以从看到ko.validation源上面异步验证,用户的输入值,而服务器验证它被视为有效。 正因为如此,在调用nestedModel2.isValid()不能为“真理”依赖。 相反,它看起来我们必须使用isValidating钩创建订阅到异步验证,只有做这样的决定,他们通知的值之后, false 。 这是设计? 相比于库中,这家似乎是最直觉,因为异步验证没有一个isValidating订阅,并可以依靠.isValid()说实话。 这也是由设计,还是我在这里做得不对呢?

Answer 1:

于是我问的问题真的有与如何在ko.validation使用异步验证的事情。 有2个,我已经从我的经验教训的大外卖:

  1. 不要创建async 匿名或一次性使用自定义规则验证器 。 相反,创建它们作为自定义规则 。 否则,你将结束我的问题描述的无限循环/坪比赛。

  2. 如果使用async验证,不信任isValid()直到所有async验证isValidating subscriptions更改为false。

如果你有多个异步验证,您可以使用类似以下的模式:

var viewModel = {
    var self = this;
    self.prop1 = ko.observable().extend({validateProp1Async: self});
    self.prop2 = ko.observable().extend({validateProp2Async: self});
    self.propN = ko.observable();
    self.isValidating = ko.computed(function() {
        return self.prop1.isValidating() || self.prop2.isValidating();
    });
    self.saveData = function(arg1, arg2, argN) {

        if (self.isValidating()) {
            setTimeout(function() {
                self.saveData(arg1, arg2, argN);
            }, 50);
            return false;
        }

        if (!self.isValid()) {
            self.errors.showAllMessages();
            return false;
        }

        // data is now trusted to be valid
        $.post('/something', 'data', function() { doWhatever() });
    }
};

您还可以看到本作与同类替代解决方案的另一个参考 。

这里是一个异步“自定义规则”的例子:

var validateProp1Async = {
    async: true,
    message: 'you suck because your input was wrong fix it or else',
    validator: function(val, otherVal, callback) {
        // val will be the value of the viewmodel's prop1() observable
        // otherVal will be the viewmodel itself, since that was passed in
        //     via the .extend call
        // callback is what you need to tell ko.validation about the result
        $.ajax({
            url: '/path/to/validation/endpoint/on/server',
            type: 'POST', // or whatever http method the server endpoint needs
            data: { prop1: val, otherProp: otherVal.propN() } // args to send server
        })
        .done(function(response, statusText, xhr) {
            callback(true); // tell ko.validation that this value is valid
        })
        .fail(function(xhr, statusText, errorThrown) {
            callback(false); // tell ko.validation that his value is NOT valid
            // the above will use the default message. You can pass in a custom
            // validation message like so:
            // callback({ isValid: false, message: xhr.responseText });
        });
    }
};

基本上,您使用callback ARG的validator功能来告诉ko.validation验证是否成功。 这一呼吁是什么将触发isValidating上经过验证的财产观测观测变回false (意思是,异步验证已经完成,并且它是目前已知的输入是否有效与否)。

如果你的服务器端验证终端在验证成功时返回一个HTTP 200(OK)状态上面会工作。 这将导致.done函数来执行,因为它是相当于$.ajax success 。 如果您的服务器在验证失败时返回一个HTTP 400(错误请求)状态,就会触发.fail函数来执行。 如果您的服务器与400返回定制验证消息回来了,你可以从xhr.responseText有效覆盖默认you suck because your input was wrong fix it or else消息。



Answer 2:

我有同样的问题,嵌套的观测与验证。 因此,一个魔术:在self.errors = ko.validation.group(self.submissionAnswers, { deep: true, live: true }); 支付额外的特别关注PARAM:包含字段对象live: true



文章来源: Knockout Validation async validators: Is this a bug or am I doing something wrong?