Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 461
MichaelvR Avatar asked Jul 18 '11 15:07

MichaelvR


People also ask

What is Ko mapping fromJS?

var viewModel = ko.mapping.fromJS(data); This automatically creates observable properties for each of the properties on data .

What is two way binding in knockout JS?

KO is able to create a two-way binding if you use value to link a form element to an Observable property, so that the changes between them are exchanged among them. If you refer a simple property on ViewModel, KO will set the form element's initial state to property value.

How do you activate a KnockoutJS model?

To activate Knockout, add the following line to a <script> block: ko. applyBindings(myViewModel); You can either put the script block at the bottom of your HTML document, or you can put it at the top and wrap the contents in a DOM-ready handler such as jQuery's $ function.

Is knockout js a framework or library?

Knockout. js is an open source library (under the MIT License) that is pure JavaScript that works with any existing web framework and every mainstream browser. Further, Knockout. js is fully documented with a complete set of online tutorials.


2 Answers

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/

like image 109
RP Niemeyer Avatar answered Sep 20 '22 06:09

RP Niemeyer


I think the solution to you problem comes from thinking about the view model in the wrong way. A view model isn't only something that delivers data to the view, but also a place holder for submitting the data back.

The way i usually end up working with knockout, i never end up sending an empty view model to the view. The view model usually has all the fields i am binding on. While they might be empty strings, or initialized objects with no display values, the actual objects to still exits, with a proper representation of each object to the fields i am binding to.

You might want to look into simply sending empty objects instead of nothing to the view.

EDIT: The example is un ASP.NET MVC

So basiclaly, i on the server side, i create an view model object, which contains all the data that needs to be displayed as well as all the data that needs to be collected. For easier validation code i generally put the data to be collected into it's own subclass, but that all a matter of the needs of your code.

In anycase, the any object going to the view inherts from a vmBase class which basically provides a toJSON() method which generates the JSON serialization of the object. This gets called in my view by the view engine. As shown in the code below.

      <script type='text/javascript'>         
        var viewModel = ko.mapping.fromJS(<%= Model.ToJson() %>);                     

        $(document).ready( function () {        
            ko.applyBindings(viewModel);                             
        });                  
     </script>   

When i am ready to send the code back up, i simply remove pull a JS version of the view model.

<script type='text/javascript'>
     var dataToSendToServer = ko.toJS(viewModel);
</script>

In some sanarios, where only a part of the view model is changing (this is if you are doing AJAX updates), you can do some cool stuff like, switching templates so that different binding can be applyed. In this case we are using a #ID_of_Container as container of the original data/template and replacing the template (which can contain data-bind="" elements) a new template ID_of_Template

<script type='text/javascript'>
    ko.cleanNode($("#ID_of_Container"));
    delete (viewModel.Some_Element_To_Be_Updated);
    viewModel = ko.mapping.updateFromJS(viewModel, New_Data_For_That_Element);

    // Use ko.toJS(viewModel) because standard template plugin doesn't understand 
    // knockout observables
    $("#ID_of_Container").html($("#ID_of_Template").tmpl(ko.toJS(viewModel)))
    ko.applyBindings(viewModel, $("#ID_of_Container")[0]);

</script>
like image 39
Sergei Golos Avatar answered Sep 23 '22 06:09

Sergei Golos