Knockout计算和输入验证

时间:2013-08-22 04:25:41

标签: javascript knockout.js

我是淘汰赛的新手,我想弄清楚如何把两件我理解的东西放在一起。

我需要:

  1. 彼此依赖的项目。
  2. 项目的输入值验证。
  3. 示例:

    • 我在几秒钟内startTime,在几秒钟内durationstopTimestartTime + duration
    • 计算
    • startTime无法更改
    • durationstopTime与输入字段相关联
    • 显示
    • stopTime并以HH:MM:SS格式
    • 输入
    • 如果用户更改stopTime,则应计算duration并自动更新
    • 如果用户更改duration,则应计算stopTime并自动更新

    我可以让它们相互更新(假设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
    });
    

    或者,我可以使用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");
    

    但我如何让他们一起工作?如果我将HMSValidate添加到第一个块中的计算中,则它不起作用,因为当HMSValidate的{​​{1}}函数获取值时,它已经被更改。

    我已经通过添加另一个observable来在第一个块中工作,该observable跟踪传递给计算器的“原始”值,然后添加另一个使用该值来计算它是否是错误状态的计算器,但是感觉不是很优雅。

    有更好的方法吗?

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

1 个答案:

答案 0 :(得分:2)

经过一周的回收问题后,我回到了这个问题,我没有找到解决方法(代码清理时间!),这就是我所拥有的。

我最终提出了我在问题的最后提到的想法,但将其封装在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;
};

这样做会创建另一个计算的observable,它作为原始observable的前置/过滤器。该observable有一些其他子可观察对象hasErrorerrorMessage,它们附加了错误状态。 rawValue跟踪输入的值,以便我们可以检测它是否是一个好的值。这可以处理我的一半要求。

至于使两个值相互依赖,我问题中的原始代码有效。为了验证它,我向它添加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");

在此处查看此行动:http://jsfiddle.net/cygnl7/tNV5S/1/

值得注意的是,write内部的验证不再是必需的,因为只有hmsValidate才能正确验证该值。

这对我来说仍然感觉有点不雅,因为我正在检查isNaN几次并且必须跟踪原始值(特别是在read()中),所以如果有人想出另一种方法来做到这一点,我全是耳朵。