Click here to Skip to main content
15,867,906 members
Articles / Programming Languages / Javascript
Tip/Trick

Replacement for ko.mapping.fromJS

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
7 May 2014CPOL 15.3K   2   2
There is a problem with ko mapping and here is how to fix it

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

C#
class Address { string Street; }
class Person { Address Home; Address Work; }  

Which I can turn into this sample JSON data

JavaScript
var json = { Home: null, Work: { Street: "1 work street" } }

Which after ko.mapping will become

JavaScript
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

JavaScript
// unlike ko.mapping this will always map the same way, 
// i.e. all properties will be mapped to an observable (recursively)
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) http://www.ansibleww.com.au
Australia Australia
The Australia born French man who went back to Australia later in life...
Finally got over life long (and mostly hopeless usually, yay!) chronic sicknesses.
Worked in Sydney, Brisbane, Darwin, Billinudgel, Darwin and Melbourne.

Comments and Discussions

 
QuestionIs there a JavaScript version of the komapperToKO function? Pin
Member 109988667-Aug-14 8:47
Member 109988667-Aug-14 8:47 
AnswerRe: Is there a JavaScript version of the komapperToKO function? Pin
Super Lloyd7-Aug-14 14:58
Super Lloyd7-Aug-14 14:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.