Using knockout.mapping I try to map from JSON to ko-object. When trying to use more advanced solutions with mapping options I often got Uncaught TypeError: g is not a function. I tried to figure out what is problem, but I am still confused.
Lets say I have pretty simple model:
var data1 = { // works
a: "a",
b: [
{ b1: "v1" },
{ b2: "v2" }
]
};
Now consuming this model works fine like this:
function viewModel ( data ) {
var self = this;
var a = ko.mapping.fromJS( data, { observe: "a"} );
return a;
}
var vm1 = viewModel( data1 ); // works
I have two properties (a and b) in my model, but I need only a to be observable, while b should just copied to the viewmodel.
Sadly it does not work so well, if data-structure gets deeper:
var data1 = { // works
a: "a",
b: [
{ b1: "v1" },
{ b2: "v2" }
]
};
var data2 = { // Uncaught TypeError: g is not a function
a: "a",
b: [
{ b1: { name: "v1" } },
{ b2: { name: "v2" } }
]
};
var data3 = { // Uncaught TypeError: g is not a function
a: "a",
b1: { name: "v1" },
b2: { name: "v2" }
};
var data4 = { // works
a: "a",
b1: "v1",
b2: "v2"
};
var data5 = { // works
a: "a",
b1: ["1::v1", "2::v1"],
b2: ["1::v2", "2::v2"]
};
function viewModel ( data ) {
var self = this;
var a = ko.mapping.fromJS( data, { observe: "a"} );
return a;
}
var vm1 = viewModel( data1 ); // works
var vm2 = viewModel( data2 ); // Uncaught TypeError: g is not a function
var vm3 = viewModel( data3 ); // Uncaught TypeError: g is not a function
var vm4 = viewModel( data4 ); // works
var vm5 = viewModel( data5 ); // works
It seems baffling that copying some structures works and other not. I understand, that problem jumps in, when there is multilevel hash somewhere in the tree.
Is this desired behavior or do I something wrong here? Why copying some structure works while other not?
I've succesfully reproduced the issue you mentioned:
var data1 = { // works
a: "a",
b: [
{ b1: "v1" },
{ b2: "v2" }
]
};
var data2 = { // Uncaught TypeError: g is not a function
a: "a",
b: [
{ b1: { name: "v1" } },
{ b2: { name: "v2" } }
]
};
function viewModel ( data ) {
var self = this;
var a = ko.mapping.fromJS( data, { observe: ["a"]} );
return a;
}
var vm1 = viewModel( data1 ); // works
var vm2 = viewModel( data2 ); // Uncaught TypeError: g is not a function
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-debug.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
As you can see, in my repro I include the non-minified versions of both libs. I stepped into it on the error, and it's here, in this code:
if(options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
{
mappedRootObject[indexer] = value(); // <---- THIS LINE
options.copiedProperties[fullPropertyName] = true;
return;
}
That line struck me as odd, because typically with KO you need to explicitly need to "unwrap" values if you're unsure if they're a plain value or an observable.
So I went to the relevant source code on Github, but to my surprise, the above code from the CDNJS version is not there, it's different:
if(options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
{
mappedRootObject[indexer] = ko.utils.unwrapObservable(value); // <-- DIFFERENT
options.copiedProperties[fullPropertyName] = true;
return;
}
That line is the way I'd expect it to be.
Then again, the most recent debug build has the buggy/first version again.
Then some more digging, I see there's issue #137 with exactly this problem. That issue ends with the same conclusion I reached: the fix is not included in the 2.4.1 build. There will not likely soon be new builds: as you can see in the project's readme this project is currently not actively being maintained.
Bottom line: you'll need to either work around this situation, or create your own custom build of mapping with this problem fixed.
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