Folliwing instructions given in the documentation, I have the following view model:
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
});
};
};
When the self.Save
method is called, an infinite loop occurs. Chrome actually reports the error as:
Uncaught RangeError: Maximum call stack size exceeded
If I use ko.mapping.toJS(self)
instead of ko.toJS(self)
, then I get a slightly more revealing error, but no real error "message":
If I swap ko.toJS(self)
out with something like { Name: self.Name(), Address: self.Address() /* etc */ }
, then everything works fine. It seems like it's trying to convert the Save
method and re-calling the method as a result.
There's either a bug in KnockoutJS or there's a problem when how I'm using it. I'd prefer the latter. Thoughts?
I found out the problem with the code. ko.toJS
preserves functions in the object so it is able to map it fine. However, jquery will invoke all functions on the data object to attempt to get a value. This in turn causes the infinite loop.
You need to flag the Save
function to not be mapped. Unfortunately the toJS
function doesn't appear to be able to do that. It will keep all members of the object. The mapping plugin fortunately allows you to do that.
To exclude a member, set the ignore
option to an array containing 'Save'
and it will ignore the Save
member when mapped.
var data = ko.mapping.toJS(self, {
ignore: ['Save']
});
If you're not using the mapping plugin, you always have the option to remove the Save
function from the JS object.
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;
};
On the other hand, I would suggest not trying to use the view model instance as the data to your calls. I would explicitly create the data object instead. Sure it has the potential to be very long, but it's an approach that will very likely always work.
self.ToData = function () {
return ko.toJS({
Name: self.Name,
Address: self.Address,
City: self.City,
State: self.State,
PostalCode: self.PostalCode
});
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With