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 thetoJS
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 theSave
member when mapped.If you're not using the mapping plugin, you always have the option to remove the
Save
function from the JS object.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.