Search code examples
javascriptknockout.jsknockout-mapping-plugin

Knockoutjs mapping and unstructured data


How to use unstructured data with knockoutjs mapping plugin? For example source json:

[
    {
        "id": 1,
        "name": "Store #1",
        "address": "City #1"
    },
    {
        "id": 2,
        "name": "Store #2"
    }
]

Store #2 without address. My template:

<ul data-bind='foreach: data'>
    <li data-bind='with: id'>
        <a href data-bind='text: name, click: function () { $parent.view($data, $index()) }'>           
        </a>
        <span data-bind='text: address'></span> 
   </li>
</ul>

My viewModel

Module.store = function () {

    var self = this;

    self.data = ko.mapping.fromJS([]);
    self.init = function () {
        $.getJSON('json/stores.json', function (stores) {
            ko.mapping.fromJS(stores, self.data);
        });
    };
};

If I run this code, I get error:

Uncaught ReferenceError: Unable to process binding "text: function (){return address }" Message: address is not defined

For Store #2

How can I set null or empty string for Store #2 address property?


Solution

  • If your view shows the address, then your viewmodel must contain that property.

    Make a viewmodel for the individual stores:

    Module.Store = function (data) {
        this.id = null;
        this.name = null;
        this.address = null;
    
        ko.mapping.fromJS(data, {}, this);
    }
    

    and a use mapping definition (see documentation) in your store list:

    Module.StoreList = function () {
        var self = this,
            mappingDefinition = {
                create: function (options) {
                    return new Module.Store(options.data);
                }
            };
    
        self.stores = ko.observableArray();
    
        self.viewStore = function (store) {
            // ...
        };
        self.init = function () {
            $.getJSON('json/stores.json', function (stores) {
                ko.mapping.fromJS(stores, mappingDefinition, self.stores);
            });
        };
    };
    

    Modified view (as a general rule, try to avoid inline functions in the view definition):

    <ul data-bind='foreach: stores'>
        <li>
            <a href data-bind='text: name, click: $parent.viewStore'></a>
            <span data-bind='text: address'></span> 
        </li>
    </ul>