Search code examples
javascriptknockout.jsjqtouchknockout-mapping-plugin

Knockout JS mapping plugin without initial data / empty form


We are using knockout and the knockout mapping plugin to facilitate databinding in our jQTouch web application. The reason we use the mapping plugin, is to be able to use knockout without the need to define/change viewmodels manually in javascript. The mapping plugin works great when you have an initial load of data from the server/client side database.

The problem we are having is that we have some screens/views which have a form in which it is possible that there isn't any initial data. Without this initial data, the mapping plugin can't 'generate' the viewmodel (ko.mapping.fromJS). This means that we still need to define our viewmodels by hand for large parts of our views.

Am I wrong in assuming that this is a scenario which the mapping plugin (should) support? I mean, this means that the mapping plugin is only usable in scenarios in which you always have an initial load of data.


Solution

  • A couple of options for you besides just manually managing your view model. The mapping plugin supports a create callback that lets you customize how it gets created. This can be used to add default properties to an object, if they happen to be missing.

    Something like this: http://jsfiddle.net/rniemeyer/WQGVC/

    Another alternative is to use a binding that creates properties that are missing. It might look like:

    //create an observable if it does not exist and populate it with the input's value
    ko.bindingHandlers.valueWithInit = {
        init: function(element, valueAccessor, allBindingsAccessor, data) {
            var property = valueAccessor(),
                value = element.value;
    
            //create the observable, if it doesn't exist 
            if (!ko.isWriteableObservable(data[property])) {
                data[property] = ko.observable();
            }
    
            //populate the observable with the element's value (could be optional)
            data[property](value);
    
            ko.applyBindingsToNode(element, { value: data[property] });
        }
    }
    

    You would use it like this (need to pass the property as a string, otherwise it will error):

    <input data-bind="valueWithInit: 'name'" />
    

    Sample here: http://jsfiddle.net/rniemeyer/JPYLp/