knockout.mapping - convert whole object included nested objects to knockout.observable

nick gowdy picture nick gowdy · Sep 18, 2013 · Viewed 8.8k times · Source

I am using knockout.mapping to convert my javascript object into a knockout observable. Inside this javascript object is a nested object and this nested object is being converted but it is in the wrong place. So when my view tries to bind the data it can't because the mapping has moved the nested object to the root.

JSON data

[
    {
        "brandName": "Knockando Single Malt Whisky",
        "categoryDescription": "Alcohol - Whisky & Bourbon",
        "dateAmended": "2008-01-28T10:05:00.000Z",
        "defaultSearchName1": "KnockandoSingleMaltWhisky",
        "defaultSearchName2": "Knockando Single Malt Whisky",
        "advertiser": {
            "activities": "Motor dealers",
            "additionalInfo": "",
            "advertiserName": "Parks of Hamilton Ltd",
            "alfRank": 3311,
            "cinemaRank": 286,
            "directMailRank": 1291,
            "internetRank": 1988,
            "notes": "Trade as Parks Motor Group.\nAlternative email: [email protected], [email protected], [email protected].",
            "numberOfEmployees": 1000,
            "outdoorRank": 1426,
            "pressRank": 5159,
            "radioRank": 675,
            "tVRank": 1810
        }
    },
    {
        "brandName": "Tesco Petrol/Diesel Fuel",
        "categoryDescription": "Petrol & Service Stations",
        "dateAmended": "2013-07-10T15:17:00.000Z",
        "defaultSearchName1": "TescoPetrol/DieselFuel",
        "defaultSearchName2": "Tesco Petrol/Diesel Fuel",
        "advertiser": {
            "activities": "Furnitures",
            "additionalInfo": "",
            "advertiserName": "Pine & Things Ltd",
            "alfRank": 4570,
            "cinemaRank": 286,
            "directMailRank": 1291,
            "internetRank": 1988,
            "notes": "Trade as P&T.\nAlternative email: [email protected].",
            "numberOfEmployees": 25,
            "outdoorRank": 1426,
            "pressRank": 2851,
            "radioRank": 2449,
            "tVRank": 1810
        }
    },
    {
        "brandName": "Tesco Electrical Products",
        "categoryDescription": "Supermarkets & Multiple Retailers",
        "dateAmended": "2013-07-10T15:52:00.000Z",
        "defaultSearchName1": "TescoElectricalProducts",
        "defaultSearchName2": "Tesco Electrical Products",
        "advertiser": {
            "activities": "Bridalwear",
            "additionalInfo": "",
            "advertiserName": "Phil Collins Bridal Collection Ltd",
            "alfRank": 4094,
            "cinemaRank": 286,
            "directMailRank": 1291,
            "internetRank": 1988,
            "notes": "Alternative email: [email protected], [email protected].",
            "numberOfEmployees": 30,
            "outdoorRank": 1426,
            "pressRank": 2487,
            "radioRank": 2449,
            "tVRank": 1810
        }
    },
    {
        "brandName": "Tesco Bank Insurance Ranges",
        "categoryDescription": "Insurance Ranges",
        "dateAmended": "2012-01-05T12:30:00.000Z",
        "defaultSearchName1": "TescoBankInsuranceRanges",
        "defaultSearchName2": "Tesco Bank Insurance Ranges",
        "advertiser": {
            "activities": "Nutition supplements",
            "additionalInfo": "",
            "advertiserName": "PhD Nutrition Ltd",
            "alfRank": 4107,
            "cinemaRank": 286,
            "directMailRank": 1291,
            "internetRank": 1988,
            "notes": "Alternative email: [email protected].\nThere is no alternative head office landline number.",
            "numberOfEmployees": 30,
            "outdoorRank": 1426,
            "pressRank": 2500,
            "radioRank": 2449,
            "tVRank": 1810
        }
    },
    {
        "brandName": "Tesco Clubcard",
        "categoryDescription": "Loyalty & Incentive Schemes",
        "dateAmended": "2008-03-26T14:05:00.000Z",
        "defaultSearchName1": "TescoClubcard",
        "defaultSearchName2": "Tesco Clubcard",
        "advertiser": {
            "activities": "",
            "additionalInfo": "",
            "advertiserName": "British Regional Airlines Group PLC",
            "alfRank": 6031,
            "cinemaRank": 286,
            "directMailRank": 1291,
            "internetRank": 1988,
            "notes": "",
            "numberOfEmployees": 0,
            "outdoorRank": 1426,
            "pressRank": 5159,
            "radioRank": 2449,
            "tVRank": 1810
        }
    },
    {
        "brandName": "Royal Mail Corporate",
        "categoryDescription": "Public Services - Corporate",
        "dateAmended": "2010-06-04T11:45:00.000Z",
        "defaultSearchName1": "RoyalMailCorporate",
        "defaultSearchName2": "Royal Mail Corporate",
        "advertiser": {
            "activities": "",
            "additionalInfo": "",
            "advertiserName": "Yorkshire Power Group Ltd",
            "alfRank": 6031,
            "cinemaRank": 286,
            "directMailRank": 1291,
            "internetRank": 1988,
            "notes": "",
            "numberOfEmployees": 0,
            "outdoorRank": 1426,
            "pressRank": 5159,
            "radioRank": 2449,
            "tVRank": 1810
        }
    },
    {
        "brandName": "E.ON Surf & Save ISP",
        "categoryDescription": "Telecom - Internet Service Providers",
        "dateAmended": "2007-11-23T12:25:00.000Z",
        "defaultSearchName1": "E.ONSurf&SaveISP",
        "defaultSearchName2": "E.ON Surf & Save ISP",
        "advertiser": {
            "activities": "",
            "additionalInfo": "",
            "advertiserName": "Stagecoach Ltd",
            "alfRank": 6031,
            "cinemaRank": 286,
            "directMailRank": 1291,
            "internetRank": 1988,
            "notes": "",
            "numberOfEmployees": 0,
            "outdoorRank": 1426,
            "pressRank": 5159,
            "radioRank": 2449,
            "tVRank": 1810
        }
    }
]

Javascript

 // Get JSON from local storage and convert to js object
 var objBrands = new Object(ko.mapping.fromJSON(localStorage.getItem('Brands')));

 // Convert from object to observable
 brands = ko.mapping.fromJS(objBrands)

My object with data before being converted to observable

enter image description here

After conversion

enter image description here

In screenshot two - advertiser is in the wrong place. I want it inside [0] but the ko.mapping parsed my object and put it there.

Answer

beauXjames picture beauXjames · Sep 18, 2013

Here's your banana for the day.

There are a couple things your could do, but i'm going to point you to the direction of using the ko.mapping plugin. Here's a fiddle I drew up using your situation as an example.

http://jsfiddle.net/beauxjames/ps75V/

First thing you'll see with your data, I did add a little json object value to start as well as loaded it into a local variable ::

var rawData = { "brand": [
{...} ]};

Next, we've got jQuery, knockout.js, and knockout.mapping.js aboard.

Then, note the function types ::

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

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

Now, inside of each there is a declaration for ko.mapping.fromJS()...which has a detailed explanation here :: http://knockoutjs.com/documentation/plugins-mapping.html

Then the mapping itself ::

var brandMapping = {
    'brands': {
        create: function (options) {
            if (options.data != null) return new BrandsViewModel(options.data);
        }
    },
    'advertiser': {
        create: function (options) {
            if (options.data != null) return new AdvertiserViewModel(options.data);
        }
    }
}

and then...the business end ::

$(document).ready(function() {
    var viewModel = new BrandsViewModel(rawData);
    ko.applyBindings(viewModel, document.getElementById('mylist'));
});

As you see...mapping these complex objects over to observables is really a snap with this plugin. Everything is tied up and you can push this to any kind of editable environment and go nuts...everything is there and you can extend your models right from within the declarations of the XXXViewModel and each instance of the object will just get smarter and smarter.

Hope this helped!