5

按照文档中给出的说明,我有以下视图模型:

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

    self.Name = ko.observable();
    self.Address = ko.observable();
    self.City = ko.observable();
    self.State = ko.observable();
    self.PostalCode = ko.observable();

    self.Save = function () {
        $.ajax({
            type: "POST",
            url: "/contact",
            data: ko.toJS(self), //infinite loop here
            success: state.OnSaved
        });
    };
};

调用该self.Save方法时,会发生无限循环。Chrome 实际上将错误报告为:

未捕获的 RangeError:超出最大调用堆栈大小

如果我使用ko.mapping.toJS(self)而不是ko.toJS(self),那么我会得到一个稍微更具启发性的错误,但没有真正的错误“消息”:

无限循环错误

如果我ko.toJS(self)用类似的东西换掉{ Name: self.Name(), Address: self.Address() /* etc */ },那么一切正常。似乎它正在尝试转换Save方法并因此重新调用该方法。

KnockoutJS 中存在错误,或者我使用它时存在问题。我更喜欢后者。想法?

4

1 回答 1

14

我发现了代码的问题。 ko.toJS保留对象中的功能,因此它能够很好地映射它。但是,jquery 将调用数据对象上的所有函数来尝试获取值。这反过来会导致无限循环。

您需要将该Save函数标记为不被映射。不幸的是,该toJS功能似乎无法做到这一点。它将保留对象的所有成员。幸运的是,映射插件允许您这样做。

要排除成员,请将ignore选项设置为包含的数组,映射时'Save'它将忽略该成员。Save

var data = ko.mapping.toJS(self, {
    ignore: ['Save']
});

如果您不使用映射插件,则始终可以选择Save从 JS 对象中删除该函数。

self.Save = function () {
    $.ajax({
        type: "POST",
        url: "/contact",
        data: self.ToData(),
        success: state.OnSaved
    });
};
self.ToData = function () {
    var data = ko.toJS(self);
    delete data.Save; // remove functions
    delete data.ToData;
    return data;
};

另一方面,我建议不要尝试将视图模型实例用作调用的数据。我会明确地创建数据对象。当然,它有可能会很长,但这种方法很可能总是有效的。

self.ToData = function () {
    return ko.toJS({
        Name: self.Name,
        Address: self.Address,
        City: self.City,
        State: self.State,
        PostalCode: self.PostalCode
    });
};
于 2012-09-05T20:24:27.100 回答