3
votes

I have got a complex model which is contains a load of ko.observable, ko.observableArray and nested objects which contain even more of these observables.

Now originally I had to make methods within each of the models which in composition make up the larger model to take Json data and then populate its observables. Now I tried the ko.mapping plugin but when I use that doing:

ko.mapping.fromJS(jsonData, {}, existingModel);

Which appears to work for most objects, however I have noticed that when I do this it seems to completely overwrite the object, and as I am using knockout js validation i.e:

this.Name = ko.observable().extend({ required: true });
this.Age = ko.observable().extend({ required: true, digits: true });

Problem is that these validation attributes seem to be removed when using the mapping module, so is there a way to get the mapping plugin to just update the values, rather than tampering with the object schema...

I am more than happy to use a different mechanism other than ko.mapping if there is a better way to apply Json data to the models.

3

3 Answers

2
votes

From http://knockoutjs.com/documentation/plugins-mapping.html (section Advanced usage)

Sometimes it may be necessary to have more control over how the mapping is performed. This is accomplished using mapping options. They can be specified during the ko.mapping.fromJS call. In subsequent calls you don’t need to specify them again.

So, for example, you can try something like this:

var self = this;
var mapping = {
    update: function(arg) {
        var data = arg.data;

        return {
            Name: data.Name && self.Name(data.Name),
            Age: data.Age && self.Age(data.Age)
        }
    }
};
ko.mapping.fromJS(data, mapping);
2
votes

I ended up having to stick with my approach although I did manage to reduce a LARGE amount of the code written to populate the existing fields by writing the simple plugin below:

https://github.com/grofit/knockout.mapping.merge

It is very simple and doesn't really do much other than just match up objects with the same name to their existing fields. I was hoping to manage child object creation, so it could populate the entire object tree, but alas I ran out of time.

Hopefully it would at least show a possible solution without having to write lots of verbose code to update a model.

0
votes

If your needs are modest, you can do this with a simple vanilla JS for loop. This example assumes that you have an existing model called 'myViewModel', and data for a partial refresh called 'jsonData':

for (var prop in jsonData) {
    if (myViewModel.hasOwnProperty(prop)) {
        myViewModel[prop](jsonData[prop]);
     }
}

As it's only updating the value, any additional attributes you've attached to the observables should remain in place.