将嵌套的knockout视图模型解析为JSON时遇到麻烦

时间:2013-01-16 18:47:33

标签: javascript jquery knockout.js viewmodel knockout-2.0

问题

当我在工作和探索knockoutjs时,我陷入了困境。我想将一个viewmodel(和底层的viewmodels)序列化为JSON。这将最终处于无限循环中,因为子视图模型具有引用父视图模型的属性。解决这个问题的最佳做法是什么?

代码

var Partner = function (parent) {
    var self = this;
    self.parent = parent;
    self.name = ko.observable('');
}

var ProjectViewModel = function () {
    var self = this;
    self.nr = ko.observable(0);
    self.tite = ko.observable('');
    self.partners = ko.observableArray();            

    self.addPartner = function () { self.partners.push(new Partner(self)) };
    self.removePartner = function (c) { self.partners.remove(c) };
};
var vm = new ProjectViewModel();
ko.applyBindings(vm);


$("#button").click(function () {
    alert(ko.toJSON(vm));
}

到目前为止我尝试了什么

我尝试在Partner viewmodel中添加以下方法:

Partner.prototype.toJSON = function () {
            var copy = ko.toJS(self);
            delete copy.parent;
            return copy;
        }

这仅适用于一个合作伙伴,如果ProjectViewModel具有多个合作伙伴,则每个合作伙伴将具有与最后一个合作伙伴相同的值。仅当我想将其序列化为JSON时才会发生这种情况。

1 个答案:

答案 0 :(得分:2)

有几种方法可以解决这种情况。 KO的ko.toJS部分正确处理此问题。它最终是JSON.stringify(在ko.toJS ko.toJSON之后调用,导致错误。

您的原型上的toJSON方法非常接近,除非您想要处理this而不是self

所以,它看起来像:

Partner.prototype.toJSON = function() {
  var copy = ko.toJS(this);
  delete copy.parent;
  return copy; 
};

处理它的其他方法:

1-实际上不会将您的父对象存储在子对象上,而是直接根据传递给构造函数的参数在任何处理程序中引用它。

var Partner = function (parent) {
    var self = this;
    self.name = ko.observable(name);

    self.doSomething = function() {
        //can use "parent" directly here without storing it anywhere
    };
};

2-“隐藏”您的父参考

var Partner = function (parent) {
    var self = this;
    self.meta = function() {};
    self.meta.parent = parent;
    self.name = ko.observable(name);    
};

您可以将parent值放在函数后面(可以作为observable的子属性)。当您的结构被ko.toJS转换为普通JS对象时,函数的任何子属性都将丢失。

3-根据您的要求改变应用程序的结构。假设父母想要在孩子改名的时候做出反应。你可以传入一个回调,设置订阅,并在它改变时执行它

var Partner = function (nameChangedCallback) {
    var self = this;
    self.name = ko.observable(name);    

    if (typeof nameChangedCallback == "function") {
        self.name.subscribe(function() {
           nameChangedCallback.call(self, self);
        });
    }
};