0

I have a problem with knockout mapping. I'm using knockout mapping plugin to represent a form that is serialized in JSON. It was working before using the knockout mapping but I need to use knockout mapping since I want my properties to be observable.

You can see the working html here : http://jsfiddle.net/etiennenoel/wG9SZ

Here's my not working javascript code:

var formData = 
    {"data": 
        [
            {
            "groupName" : "Properties",
            "content" : 
            [
                {
                    "title" : "Calculation Method",
                    "formType" : "select",
                    "value" : 
                    [
                        {
                            "title" : "Voltage Drop - Unbalanced",
                            "selected" : true
                        },
                        {
                            "title" : "Voltage Drop - Balanced"
                        }
                    ]
                },

                {
                    "title" : "Tolerance (% V)",
                    "formType" : "textBox",
                    "value" : 0.01
                },

                {
                    "title" : "Calculation Options",
                    "formType" : "radio",
                    "value" : 
                    [ 
                        {
                            "title" : "Flat Start (at Nominal Conditions",
                            "checked" : false
                        } ,
                        {
                            "title" : "Assume Line Transposition",
                            "checked" : true
                        }
                    ]
                },

                {
                    "title" : "Adjust Conductor Resistance at",
                    "formType" : "textBox",
                    "disabled" : true,
                    "value" : 77,
                    "appendLabel" : true,
                    "appendLabelText" : "°F"    
                }
            ]
            },
            {
            "groupName" : "Properties",
            "content" : 
            [
                {
                    "title" : "Calculation Method",
                    "formType" : "select",
                    "value" : 
                    [
                        {
                            "title" : "Voltage Drop - Unbalanced",
                            "selected" : true
                        },
                        {
                            "title" : "Voltage Drop - Balanced"
                        }
                    ]
                },

                {
                    "title" : "Tolerance (% V)",
                    "formType" : "textBox",
                    "value" : 0.01
                },

                {
                    "title" : "Calculation Options",
                    "formType" : "radio",
                    "value" : 
                    [ 
                        {
                            "title" : "Flat Start (at Nominal Conditions",
                            "checked" : false
                        } ,
                        {
                            "title" : "Assume Line Transposition",
                            "checked" : true
                        }
                    ]
                },

                {
                    "title" : "Adjust Conductor Resistance at",
                    "formType" : "textBox",
                    "disabled" : true,
                    "value" : 77,
                    "appendLabel" : true,
                    "appendLabelText" : "°F"    
                }
            ]
            }
        ]
    };  
ko.mapping.fromJS(formData);

Here's the jsfiddle of the same code: http://jsfiddle.net/etiennenoel/wG9SZ/3/

What is the problem between when I use mapping and when I don't use it ?

Etienne Noël
  • 5,988
  • 6
  • 48
  • 75

3 Answers3

1

In your second case you forgot to ApplyBindings.

ko.applyBindings(formData);
Bill Gregg
  • 7,067
  • 2
  • 22
  • 39
1

I don't know if this is the case with your scenario, but it's worth a post.

I had issues with the mapping plugin, when I had a more complex viewmodel with nested properties or lists. It turned out that after mapping to an already constructed viewmodel, the sub-objects were no more observables. With this issue, for me this code worked, what I've found somewhere (unfortunately I already really don't know where). I called this function for my viewmodel after mapping to that.

function makeAllObservables(observable) {
    // Loop through its children
    for (var child in observable()) {
        // If this child is not an observable and is an object
        if ((!ko.isObservable(observable()[child])) && (typeof observable()[child] === "object")) {
            // Make it an observable
            observable()[child] = ko.observable(observable()[child]);
            // Make all of its children observables
            makeAllObservables(observable()[child]);
        }
    }
};

Usage (when updating the model from server response, the first line should not be there):

var model = ko.observable({});
ko.mapping.fromJS(myJSObject, {}, model);
makeAllObservables(model);
ko.applyBindings(model);

I ment mapping to an already constructed viewmodel for example, when you want to update your viewmodel with new JSON data from server. In that case I lost nested bindings without the code above.

UPDATE: I've found the source where I borrowed the technique from, here. Note that I slightly modified that code in that post, because somehow that was not working for me.

Community
  • 1
  • 1
Zoltán Tamási
  • 12,249
  • 8
  • 65
  • 93
  • How do you call it exactly. I mean, what is your observable ? What would it be in my case, ko.mapping.fromJS(formData)? – Etienne Noël Jun 20 '13 at 20:09
  • why do you have that portion of the line: , $('#myform').get(0)); – Etienne Noël Jun 20 '13 at 20:14
  • @CoachNono, because in my case I had more than one forms, and I want to apply this binding only for a specific one. The .get(0) will return the first matched FORM from the jQuery result. – Zoltán Tamási Jun 20 '13 at 20:15
  • when I'm doing that, I don't see my forms... http://jsfiddle.net/etiennenoel/wG9SZ/5/ – Etienne Noël Jun 20 '13 at 20:16
  • Yes, because you don't have a form with id "myform". I think you should not use that second parameter. It's only required when you want to apply the bindings inside a specific HTML element. Sorry for confusing, I've removed that from my post. – Zoltán Tamási Jun 20 '13 at 20:19
  • I deleted it but still they don't show ... thanks for your help by the way – Etienne Noël Jun 20 '13 at 20:20
  • Sorry, I've corrected another mistake of mine, I left a "self." qualifier inside the function at the recursive call. I copied it from my original code, that's why I missed it. – Zoltán Tamási Jun 20 '13 at 20:26
  • Your code works, however, it is not showing the select items... http://jsfiddle.net/etiennenoel/wG9SZ/15/ – Etienne Noël Jun 20 '13 at 20:29
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/32107/discussion-between-zoltan-tamasi-and-coachnono) – Zoltán Tamási Jun 20 '13 at 20:29
1

You need to bind the mapped viewmodel to the view:

ko.applyBindings(ko.mapping.fromJS(formData));

and since everything is now an observable the logic in the view needs to be changed to use the method syntax:

<!-- ko if: $data.formType() === "select" -->

To get the options to display, you need to tell knockout what the property name is on the object:

<select data-bind="options: $data.value, optionsText: 'title'"></select>

DaveB
  • 9,470
  • 4
  • 39
  • 66