Knockout嵌套对象范围未定义参考

时间:2014-06-15 05:02:52

标签: javascript knockout.js scope

我正在尝试使用KnockoutJS使用以下代码编写基本表单:

var Form = function() {
    var self = this;
    self.name = {
        value: ko.observable(""),
        isValid: ko.computed(function () {
            return self.name.value().length > 0;
        }),
    },
    self.email = {
        value: ko.observable(""),
        isValid: ko.computed(function () {
            return false;
        })
    },
    self.message = {
        value: ko.observable(""),
        isValid: ko.computed(function () {
            return false;
        })
    },
    self.isValid = ko.computed(function () {
        return self.name.isValid && self.email.isValid && self.message.isValid;
    })
};

但是,当我尝试运行此代码时,出现以下错误:Unable to get property 'value' of undefined or null reference。对我来说,这看起来像是一个范围问题,但我对Knockout不够熟悉,无法理解为什么会发生这种情况。没有Knockout我似乎能够使这个代码工作(用isValid的值和函数替换所有的observable),但我希望实时更新这些值。我可以总是将验证功能分离到顶级功能,但这似乎是不正确的方法。每个表单字段都有一个值,并且它有自己唯一的验证,因此将每个表单字段作为两个属性的自己的对象是有意义的。

感谢任何帮助或指导。

2 个答案:

答案 0 :(得分:2)

这个

self.name = {
    value: ko.observable(""),
    isValid: ko.computed(function () {
        return self.name.value().length > 0;
    }),
},

不起作用,因为内部函数(计算的回调)引用self.name,甚至还没有完全定义。由于立即调用计算机,您会看到错误。

可以使用deferEvaluation option延迟评估isValid计算,直到实际请求其值为止:

self.name = {
    value: ko.observable(""),
    isValid: ko.computed({
        read: function () { return self.name.value().length > 0; },
        deferEvaluation: true
    }),
},

这样可行,但它也很快就会变得重复和笨拙。


作为替代方案,您可以使用Knockout extenders以抽象/分离的方式创建可观察的可验证值。

ko.extenders.validator = function (target, validationCallback) {
    // create an isValid property on the target observable, 
    // immediately calculate validity of its current value
    target.isValid = ko.observable(validationCallback(target()));

    // subscribe to future changes
    target.subscribe(function (newValue) {
        target.isValid(validationCallback(newValue));
    });

    // return target observable for chaining
    return target;
};

现在定义了这个扩展器,你需要做的就是创建回调函数,验证一个值并返回true或false。

var Form = function () {
    var self = this;
    self.name = ko.observable("").extend({
        validator: function (value) { return value.length > 0; }
    });
    self.email = ko.observable("").extend({
        validator: function (value) { return true; }
    });
    self.message = ko.observable("").extend({
        validator: function (value) { return true; }
    });
    self.isValid = ko.computed(function () {
        var overallValid = true;
        ko.utils.objectForEach(self, function (key, value) {
            if (value.hasOwnProperty("isValid")) {
                overallValid = overallValid && value.isValid();
            }
        });
        return overallValid;
    });
};

现在,进一步分离验证功能变得非常简单:

var validators = {
    email: function (value) { return /* email check here */; },
    minLen: function (minLen) {
        return function (value) { return value.length >= minLen; }
    },
    maxLen: function (maxLen) {
        return function (value) { return value.length <= maxLen; }
    },
    minmaxLen: function (minLen, maxLen) {
        return function (value) { return value.length >= minLen && value.length <= maxLen; }
    },
    matches: function (regex) {
        return function (value) { return regex.test(value); }
    }
}

self.name = ko.observable("").extend({ validator: validators.minLen(1) });
self.age = ko.observable("").extend({ validator: validators.matches(/^\d+$/) });

你会发现somebody already did all of this(以及更多)。

答案 1 :(得分:0)

最好为每个对象创建单独的函数:

var Form = function() {
    var self = this;

    self.name = new NameFormField();
}

var NameFormField = function() {
    var self = this;

    self.value = ko.observable("");
    self.isValid = ko.computed(function () {
        //not sure why you are checking length here
        return self.value().length > 0;
    });
}

你能让它发挥作用的另一种方法是:

var Form = function() {
    var self = this;

    self.name = {
        value: ko.observable("")
    };

    self.name.isValid = ko.computed(function () {
        //not sure why you are checking length here
        return self.name.value().length > 0;
    });