Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout.js mapping not binding child models

I'm having an issue with knockout.js and the mapping plugin not creating models for child arrays in the source data

var data = {
    outer: [
        {
        'id': 1,
        name: 'test outer',
        inner: [{
            'id': 1,
            name: 'test inner'}]}]
};

function OuterModel(data) {
    var self = this;
    ko.mapping.fromJS(data, {}, this);

    self.fullText = ko.computed(function() {
        return self.id() + ". " + self.name();
    });
}

function InnerModel(data, parent) {
    var self = this;
    ko.mapping.fromJS(data, {}, this);

    self.fullText = ko.computed(function() {
        return self.id() + ". " + self.name() + "(" + parent + ")";
    });
}

function PageModel() {
    var self = this;
    this.data = null;
}

var mapping = {
    'outer': {
        key: function(data) {
            return ko.utils.unwrapObservable(data.id);
        },
        create: function(options) {
            var thisModel = new OuterModel(options.data);
            return thisModel;
        }
    },
    'inner': {
        key: function(data) {
            return ko.utils.unwrapObservable(data.id);
        },
        create: function(options) {
            var thisModel = new InnerModel(options.data);
            return thisModel;
        }
    }
};

var model = new PageModel();
model.data = ko.mapping.fromJS(data, mapping);

(This is a small of a repo I can make for this. Fiddle available here: http://jsfiddle.net/msieker/6Wx3s/)

In short, the InnerModel constructor is never called. I've tried this with the 'InnerModel' both where it is in this snippet, and within the 'inner' mapping. From most accounts of what I've seen, this should just work, but obviously I'm missing something.

Anyone have experience with this that can point me in the right direction?

EDIT: Based on @John Earles answer, I've gotten this a bit closer to what I need:

var data = {
    outer: [
        {
        'id': 1,
        name: 'test outer',
        inner: [{
            'id': 1,
            name: 'test inner'}]}]
};

var outerMapping = {
    'outer': {
        key: function(data) {
            return ko.utils.unwrapObservable(data.id);
        },
        create: function(options) {
            var thisModel = new OuterModel(options.data);
            return thisModel;
        }
    }
};

function OuterModel(data) {
    var self = this;
    ko.mapping.fromJS(data, innerMapping, this);

    self.fullText = ko.computed(function() {
        return self.id() + ". " + self.name();
    });
}

var innerMapping = {
    'inner': {
        key: function(data) {
            return ko.utils.unwrapObservable(data.id);
        },
        create: function(options) {
            console.log(options);
            var thisModel = new InnerModel(options.data, options.parent());
            return thisModel;
        }
    }
};

function InnerModel(data, parent) {
    var self = this;
    ko.mapping.fromJS(data, {}, this);

    self.fullText = ko.computed(function() {
        return self.id() + ". " + self.name() + "(" + parent + ")";
    });
}

function PageModel() {
    var self = this;
    this.data = null;
}

var model = new PageModel();
model.data = ko.mapping.fromJS(data, outerMapping);
ko.applyBindings(model);?

However, the parent that gets passed to InnerModel is null, which getting that is the whole reason I'm pursuing the mapping plugin. The docs lead me to believe that this should be getting getting passed in on the options parameter to the create function, but instead I get an observable whose value is null. Any additional pointers in this direction?

like image 315
Matt Sieker Avatar asked Mar 21 '12 07:03

Matt Sieker


2 Answers

You are not passing your mapping options (which defines how to map inner) into the OuterModel mapping call.

function OuterModel(data) {
    var self = this;
    ko.mapping.fromJS(data, mapping, this);

    self.fullText = ko.computed(function() {
        return self.id() + ". " + self.name();
    });
}

EDIT: Updated fiddle to show manual attachment of parent:

http://jsfiddle.net/jearles/6Wx3s/5/

like image 101
John Earles Avatar answered Oct 06 '22 18:10

John Earles


I had a very similar scenario a while back, check out the Q & A here: Map JSON data to Knockout observableArray with specific view model type

In my example I have a StateViewModel which contains an observableArray populated with 2 CityViewModels, and each CityViewModel contains an observableArray populated with 2 StreetViewModels.

I then wanted this view model structure to be constructed from my hierarchical json data using knockout's mapping plugin.

This fiddle maps out the answer: http://jsfiddle.net/KodeKreachor/pTEbA/2/

like image 25
KodeKreachor Avatar answered Oct 06 '22 18:10

KodeKreachor