简体   繁体   English

Knockout计算和输入验证

[英]Knockout computed and input validation

I am fairly new to knockout and am trying to figure out how to put two pieces that I understand together. 我是淘汰赛的新手,我想弄清楚如何将两件我理解的东西放在一起。

I need: 我需要:

  1. Items that are dependent on each other. 相互依赖的项目。
  2. Input value validation on the items. 对项目的输入值验证。

Example: 例:

  • I have startTime in seconds, duration in seconds, and stopTime that is calculated from startTime + duration 我有以秒为单位的startTime以秒为单位的duration以及从startTime + duration计算的stopTime
  • startTime cannot be changed startTime无法更改
  • duration and stopTime are tied to input fields durationstopTime与输入字段相关联
  • stopTime is displayed and entered in HH:MM:SS format 显示stopTime并以HH:MM:SS格式输入
  • If the user changes stopTime , duration should be calculated and automatically updated 如果用户更改stopTime ,则应计算并自动更新duration
  • If the user changes duration , stopTime should be calculated and automatically updated 如果用户更改duration ,则应计算并自动更新stopTime

I can make them update each other (assume Sec2HMS and HMS2Sec are defined elsewhere, and convert between HH:MM:SS and seconds): 我可以让它们互相更新(假设Sec2HMSHMS2Sec在别处定义,并在HH:MM:SS和秒之间转换):

this.startTime = 120; // Start at 120 seconds
this.duration = ko.observable(0);

// This dependency works by itself.
this.stopTimeFormatted = ko.computed({
    read: function () {
        return Sec2HMS(this.startTime + parseInt(this.duration()), true);
    },
    write: function (value) {
        var stopTimeSeconds = HMS2Sec(value);
        if (!isNaN(stopTimeSeconds)) {
            this.duration(stopTimeSeconds - this.startTime);
        } else {
            this.duration(0);
        }
    },
    owner: this
});

Or, I can use extenders or fn to validate the input as is shown in the knockout docs: 或者,我可以使用extendersfn验证输入,如敲除文档中所示:

ko.subscribable.fn.HMSValidate = function (errorMessage) {
    //add some sub-observables to our observable
    var observable = this;
    observable.hasError = ko.observable();
    observable.errorMessage = ko.observable();

    function validate(newValue) {
        var isInvalid = isNaN(HMS2Sec(newValue));
        observable.hasError(isInvalid ? true : false);
        observable.errorMessage(isInvalid ? errorMessage : null);
    }

    //initial validation
    validate(observable());

    //validate whenever the value changes
    observable.subscribe(validate);

    //return the original observable
    return observable;
};
this.startTime = 120; // Start at 120 seconds
this.duration = ko.observable(0);
this.stopTimeHMS = ko.observable("00:00:00").HMSValidate("HH:MM:SS please");

But how do I get them working together? 但是我如何让他们一起工作呢? If I add the HMSValidate to the computed in the first block it doesn't work because by the time HMSValidate 's validate function gets the value it's already been changed. 如果我将HMSValidate添加到第一个块中的计算中它不起作用,因为当HMSValidatevalidate函数获得它已经被更改的值时。

I have made it work in the first block by adding another observable that keeps track of the "raw" value passed into the computed and then adding another computed that uses that value to decide if it's an error state or not, but that doesn't feel very elegant. 我通过添加另一个observable使其在第一个块中工作,该observable跟踪传递给计算器的“原始”值,然后添加另一个使用该值的计算器来判断它是否是错误状态,但是没有感觉非常优雅。

Is there a better way? 有没有更好的办法?

http://jsfiddle.net/cygnl7/njNaS/2/ http://jsfiddle.net/cygnl7/njNaS/2/

I came back to this after a week of wrapping up issues that I didn't have a workaround for (code cleanup time!), and this is what I have. 经过一个星期的回收问题后,我回到了这个问题,我没有解决方法(代码清理时间!),这就是我所拥有的。

I ended up with the idea that I mentioned in the end of the question, but encapsulating it in the fn itself. 我最终提出了我在问题的最后提到的想法,但是将其封装在fn本身中。

ko.subscribable.fn.hmsValidate = function (errorMessage) {
    var origObservable = this;
    var rawValue = ko.observable(origObservable()); // Used for error checking without changing our main observable.
    if (!origObservable.hmsFormatValidator) {
        // Handy place to store the validator observable
        origObservable.hmsFormatValidator = ko.computed({
            read: function () {
                // Something else could have updated our observable, so keep our rawValue in sync.
                rawValue(origObservable());
                return origObservable();
            },
            write: function (newValue) {
                rawValue(newValue);
                if (newValue != origObservable() && !isNaN(HMS2Sec(newValue))) {
                    origObservable(newValue);
                }
            }
        });
        origObservable.hmsFormatValidator.hasError = ko.computed(function () {
            return isNaN(HMS2Sec(rawValue()));
        }, this);
        origObservable.hmsFormatValidator.errorMessage = ko.computed(function () {
            return errorMessage;
        }, this);
    }

    return origObservable.hmsFormatValidator;
};

What this does is creates another computed observable that acts as a front/filter to the original observable. 这样做会创建另一个计算的observable,它作为原始observable的前置/过滤器。 That observable has some other sub-observables, hasError and errorMessage , attached to it for the error states. 该observable有一些其他的子可观察对象, hasErrorerrorMessage ,附加到它的错误状态。 The rawValue keeps track of the value as it was entered so that we can detect whether it was a good value or not. rawValue会跟踪输入的值,以便我们可以检测它是否是一个好的值。 This handles the validation half of my requirements. 这可以处理我的一半要求。

As for making two values dependent on each other, the original code in my question works. 至于使两个值相互依赖,我问题中的原始代码有效。 To make it validated, I add hmsValidate to it, like so: 为了验证它,我添加了hmsValidate ,如下所示:

this.stopTimeFormatted = ko.computed({
    read: function () {
        return Sec2HMS(this.startTime + parseInt(this.duration()), true);
    },
    write: function (value) {
        this.duration(HMS2Sec(value) - this.startTime);
    },
    owner: this
}).hmsValidate("HH:MM:SS please");

See it in action here: http://jsfiddle.net/cygnl7/tNV5S/1/ 在这里看到它: http//jsfiddle.net/cygnl7/tNV5S/1/

It's worth noting that the validation inside of write is no longer necessary since the value will only ever be written by hmsValidate if it validated properly. 值得注意的是, write内部的验证不再是必要的,因为只有在正确验证的情况下,该值才会由hmsValidate写入。

This still feels a little inelegant to me since I'm checking isNaN a couple of times and having to track the original value (especially in the read()), so if someone comes up with another way to do this, I'm all ears. 这对我来说仍然感觉有点不雅,因为我正在检查isNaN几次并且必须跟踪原始值(特别是在read()中),所以如果有人想出另一种方法来做到这一点,我都是耳朵。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM