Knockout.js mapping not binding child models
Asked Answered
A

2

6

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?

Abandon answered 21/3, 2012 at 7:36 Comment(2)
Unfortunately parent is not going to do what you want. Parent in this case gives your the array that the new item is part of, not the object that contains that array. You'll need to make the parent association yourself. Like this: jsfiddle.net/jearles/6Wx3s/5Democratic
you can try this #9952021 This solved my issues with child-parent mappingTrophic
D
2

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/

Democratic answered 21/3, 2012 at 10:10 Comment(0)
K
1

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/

Kowalczyk answered 3/4, 2012 at 21:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.