knockout.jsknockout-mapping-plugin

Knockout mapping plugin - making all objects observable


I have the following Knockout viewmodel:

var myViewModel = {
    courseData: ko.observable(null);
};

I then receive the following data from the server:

var data = {
  "courses": [
    {
      "srcCourses": [ { "code": "001", "name": "Course 1"}, { "code": "002", "name": "Course 2"} ],
      "destCourse": { "code": "003", "name": "Course 3"}
    },
    {
      "srcCourse": [ { "code": "004", "name": "Course 4"}, { "code": "005", "name": "Course 5"} ],
      "destCourse": { "code": "006", "name": "Course 6"}
    }
  ]
}

I run is through the Knockout mapping plugin into my view model as follows:

this.courseData(ko.mapping.fromJS(data));

This essentially leaves me with the following view model object hierarchy:

{
  KnockoutObservable(this.courseData): {
    KnockoutObservableArray(Courses): [
      {
        KnockoutObservableArray(srcCourses): [ ... ],
        destCourse: { KnockoutObservable(code), KnockoutObservable(name) }
      },
      {
        ...
      }
    ]
  }
}

The problem is that the "destCourse" object remains an object and does not get converted to observable (although every property within the destCourse object gets converted to observable).

How can I make all child objects the data be converted to observable too? Ideally in the most generic way possible?


Solution

  • I've come across this before and I've never been able to work out why knockout does this with child objects.

    Anyway, you just need to give the mapper a bit of guidance as described in the docs

    var coursesMapping = {
      "destCourse":{
        create:function(options){
          return ko.observable(ko.mapping.fromJS(options.data));
        }
      }
    }
    
    var mapped = ko.mapping.fromJS(data, coursesMapping);
    

    Here's a snippet:

    var data = {
      "courses": [
        {
          "srcCourses": [ { "code": "001", "name": "Course 1"}, { "code": "002", "name": "Course 2"} ],
          "destCourse": { "code": "003", "name": "Course 3"}
        },
        {
          "srcCourse": [ { "code": "004", "name": "Course 4"}, { "code": "005", "name": "Course 5"} ],
          "destCourse": { "code": "006", "name": "Course 6"}
        }
      ]
    }
    
    var coursesMapping = {
      "destCourse":{
        create:function(options){
          return ko.observable(ko.mapping.fromJS(options.data));
        }
      }
    }
    
    var mapped = ko.mapping.fromJS(data,coursesMapping);
    
    console.log(mapped.courses()[0].destCourse().name()); // "Course 3"
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>