Introduction
About mapping business model to Knockout observable
Background
In my app I turn my (C#) business model with JSON converter into Javascript object. Then I use ko.mapping.fromJS to turn them into observable I ran into the fact that ko.mapping is inconsistent!
(If you don't know what are observables and/or ko.mapping, have a look at Knockout)
Allow me to illustrate, say I have this C# model
class Address { string Street; }
class Person { Address Home; Address Work; }
Which I can turn into this sample JSON data
var json = { Home: null, Work: { Street: "1 work street" } }
Which after ko.mapping will become
var kojson = ko.mapping.fromJS(json)
var json = { Home: ko.observable(), Work: { Street: ko.observable("1 work street") } }
See the problem? Home
is now an observable property, whereas Work
is a normal object (with observable properties)
Using the code
To solve this problem I wrote a replacement function, here is the (TypeScript) version
function komapperToKO(src) {
var mapped: Array<{ src; dst; }> = [];
var map = function (obj) {
obj = ko.unwrap(obj);
if (obj === null
|| obj === undefined
|| typeof obj === "number"
|| typeof obj === "boolean"
|| typeof obj === "string"
|| typeof obj === "function"
) {
return obj;
}
else if (obj instanceof Array) {
var ares = [];
for (var i = 0; i < obj.length; i++) {
ares.push(map(obj[i]));
}
return ko.observableArray(ares);
}
else {
var prev = mapped.enumerable().first(x => x.src === obj);
if (prev)
return prev.dst;
var res = <any>{};
mapped.push({ src: obj, dst: res });
for (var p in obj) {
var pv = ko.unwrap(obj[p]);
if (typeof pv === "function") {
res[p] = pv;
}
else if (pv instanceof Array) {
res[p] = map(pv);
}
else {
res[p] = ko.observable(map(pv));
}
}
return res;
}
}
return map(src);
}
Points of Interest
A little dive inside Knockout
History
Keep a running update of any changes or improvements you've made here.