Niel de Wet
Niel de Wet

Reputation: 8416

Using knockout mapping, how do I map a whole array attribute myself?

From my server I receive an object that looks something like this:

var fromServer = { 
    foo: "Some foo", 
    barArray: [ "bar1", "bar2", "bar3", "bar4", "bar5" ] 
};

Using the mapping plugin for knockout, I want to customize how the view model is constructed and break the array up into sub-arrays of length 2. Something like this:

viewModel: {
    foo: "Some foo",
    barArray: [ ["bar1", "bar2"], ["bar3", "bar4"], ["bar5"] ]
}

On my way there, I realized that that the create callback is called once per array item. Here is my view:

<h1 data-bind="text: foo"></h1>
<ul data-bind="foreach: barArray">
    <li data-bind="text: $data.name"></li>
</ul>

And the javascript:

var fromServer = { foo: "Some foo", barArray: [ "bar1", "bar2", "bar3", "bar4", "bar5" ] };

var mapping = {
    'barArray': { 
        create: function(options) {
            return new barModel("This is " + options.data);
        }
    }
};

var barModel = function(data) {
   this.name = data; 
};

var viewModel = ko.mapping.fromJS(fromServer, mapping);

ko.applyBindings(viewModel);

Here's a fiddle showing the behaviour

Here's the real question: Using the create callback, can I access the whole array, rather than each element, one at at time? Or, am I approaching this in the wrong way?

Upvotes: 0

Views: 221

Answers (3)

Rustam
Rustam

Reputation: 746

You can restructure your incoming array the way you would use it before creating your view model. Rather than using array of data that are 'sematically grouped' in some way, you can turn it into array of objects with properties filled accordingly.

I assume your data somehow related to weekdays, so I created WeekViewModel that will hold data for each week day. Before mapping data to viewmodel, I process if with ProcessBeforeCreatingViewModel function:

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

function ProcessBeforeCreatingViewModel(data) {

 var tempObj = null;

 var data_processed = {
    foo: data ? data.foo : '',
    barArray: []
 };

 if (data && data.barArray) {

    data.barArray.forEach(function (barItem, index) {

        if (index % days.length == 0) {
            tempObj = {};
            days.forEach(function (dayName) {
                tempObj[dayName] = undefined;
            });
            data_processed.barArray.push(tempObj);
        }

        tempObj[days[index]] = barItem;
    });
 }

 return data_processed;
}


var mapping = {
    'barArray': { 
        'create': function(options) {
            return new WeekViewModel(options.data);
        }
    }
};

var fromServer_Processed = ProcessBeforeCreatingViewModel(fromServer);

var viewModel = ko.mapping.fromJS(fromServer_Processed, mapping);

ko.applyBindings(viewModel);

Here is a fiddle: http://jsfiddle.net/JeJKP/

Upvotes: 0

Pablo Rodr&#237;guez
Pablo Rodr&#237;guez

Reputation: 436

You need to do this, for mapping objects from the root of the JSON

        var mapping = {
            '': { 
                create: function(options) {
                    return new .....
                }
            }
        };

Upvotes: 1

Paul Manzotti
Paul Manzotti

Reputation: 5147

Looking at the documentation, the options parameter passed into the create method has two properties:

data: The JavaScript object containing the data for this child

parent: The parent object or array to which this child belongs

so you can access it using options.parent.

Upvotes: 0

Related Questions