Reputation: 4608
As I was working and exploring knockoutjs, I got stuck at some point. I want to serialize a viewmodel (and underlying viewmodels) to JSON. This will end up in an infinite loop because the child viewmodels have a property which references the parent viewmodel. What is the best practice for solving this issue?
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));
}
I tried adding the following method in the Partner viewmodel:
Partner.prototype.toJSON = function () {
var copy = ko.toJS(self);
delete copy.parent;
return copy;
}
This works with only one Partner, if the ProjectViewModel has multipe partners, every partner will have the same value as the last partner. This happens only when I want to serialize it to JSON.
Upvotes: 2
Views: 560
Reputation: 114792
There are several ways to handle this situation. The ko.toJS
part of KO handles this properly. It is ultimately JSON.stringify
(called after ko.toJS
in ko.toJSON
that causes the error.
Your toJSON
method on your prototype is pretty close, except that you want to be dealing with this
rather than self
.
So, it would look like:
Partner.prototype.toJSON = function() {
var copy = ko.toJS(this);
delete copy.parent;
return copy;
};
Other ways to handle it:
1- don't actually store your parent on the child object and just reference it in any handlers directly based on the argument passed to the constructor.
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- "hide" your parent reference
var Partner = function (parent) {
var self = this;
self.meta = function() {};
self.meta.parent = parent;
self.name = ko.observable(name);
};
You can place your parent
value behind a function (could be as a sub-property of an observable). When your structure is turned into a plain JS object by ko.toJS
, then any sub-properties of functions are lost.
3- change the structure of your application, depending on what you want to do. Suppose that the parent wants to react whenever a child has it's name changed. You could pass in a callback, setup the subscription, and execute it whenever it changes
var Partner = function (nameChangedCallback) {
var self = this;
self.name = ko.observable(name);
if (typeof nameChangedCallback == "function") {
self.name.subscribe(function() {
nameChangedCallback.call(self, self);
});
}
};
Upvotes: 2