I'm experimenting with knockout and the mapping plugin and wondering why this doesn't work. I have a viewmodel that I want to load using the mapping extension
function todoListViewModel(data) {
ko.mapping.fromJSON(data, { todos: TodoItem.options }, self);
ko.mapping.fromJSON(data, { todos: TodoItem.options }, self);
}
the mapping has an options that looks like this:
var TodoItem = function (options) {
var todoItem = ko.mapping.fromJS(options.data);
todoItem.remove = function () {
alert('remove');
};
return todoItem;
};
TodoItem.options = {
create: TodoItem
};
And the JSON data looks like:
{
"id": "0",
"todo": "",
"todos": [
{
"todo": "Kevin",
"isDone": true
}
]
}
The first call to the mapping succeeds but the second call fails with a stackoverflow: ("Uncaught RangeError: Maximum call stack size exceeded" in Chrome)
If I change the code so that I don't pass an options to the mapping then the exception is not thrown.
I've also tried simplifying the ToDo constructor to this
var TodoItem = function (options) {
var todoItem = {};
return todoItem;
};
but I still get the same error.
It looks like I can't do this but I was wondering why?
The problem is with this bit:
You're extending TodoItem with a property "options", that again contains TodoItem - so the function TodoItem has a property that contains itself.
Because it's only a reference, that's no problem in JavaScript. But the knockout mapping plugin seems to do some copy action (or something with the same effect) on that option object, i.e. it tries to iterate over all its properties and thus ends up in an endless loop.
You can easily resolve this by using an anonymous function as a wrapper: