Search code examples
javascriptknockout.jstreeknockout-mapping-plugin

knockoutjs mapping: adding observables in a tree


I'm using knockoutjs to visualize a tree. I have a initial AJAX-call to load the hole tree. Mapping happens using the knockout-mapping-plugin. Everything is working fine :) thanks for this great piece of software.

My problem is: I want to add additional observables to the model. I can do this using mapping options.

Object:

{ id: ko.observable(), parentID: ko.observable(), description: ko.observable(), children: ko.observableArray() }

Mappingoptions:

var mappingOptions = {
            create: function (options) {
                console.log("create");
                var vm = ko.mapping.fromJS(options.data);
                vm.newObservable = ko.observable(true);
                return vm;
            }
        };

Mapping:

ko.mapping.fromJS(response, mappingOptions, self.nodes);

response is the list of objects(nodes). self.nodes is the observableArray() holding the list of objects(nodes)

Every node has the children-observalbeArray containing also nodes(with children) So nothing special, that's basically how a tree works :)

But this only adds this new observable to the root node. I want this extra observable also in every child and child's child (and so on...).

I tried:

var mappingOptions = {
    create: function (options) {
        console.log("create");
        var vm = ko.mapping.fromJS(options.data);
        vm.newObservable = ko.observable(true);
        return vm;
    },
    'children': {
        create: function (options) {
            console.log("children.create");
            options.data.newObservable = ko.observable(true);
            return ko.mapping.fromJS(options.data);                    
        }
    }
};

I found this somewhere on the internet. It is not working.

Can someone help me?

Thanks!

P.S.: i can't provide a fiddle, because the service seems to be broken right know (or my firewall is blocking it :( )


Solution

  • I would try creating a data model. Let's call it Node:

    var Node = function Node(data) {
      this.parentID    = ko.observable( data.parentID );
      this.description = ko.observable( data.description );
      this.children    = ko.observableArray([]);
    
      data.children.forEach(function(child) {
        this.children.push( new Node(child) );
      }, this);
    }
    

    This makes it easy to create any additional properties on every node you wish.

    In your viewmodel:

    var Viewmodel = function Viewmodel() {
      this.nodes = ko.observableArray([]);
    
      this.getResponse = function getResponse() {
        // response is loaded here
    
        response.forEach(function(node) {
          this.nodes.push( new Node(node) );
        }, this);
      };
    }
    

    Please note that I'm using the native Array.prototype.forEach here. If you need to support IE < 9, you can replace it with Knockouts ko.utils.arrayForEach instead.