Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding properties to the view model created by using the Knockout JS mapping plugin

Tags:

knockout.js

I am working through the mapping plugin example on the Knockoutjs website.

This is the example data.

Knockout JS Mapping Plugin

var data = {
    name: 'Scott',
    children: [
        { id : 1, name : 'Alice' }
    ]
}

The example shows how to override the mapping for one of the children but how do I alter the mapping for the base object.

If for example I wanted to add a "FavouriteChild" property to Scott how would I go about it?

I assume I need to use the create function on the base mapping but I cannot find an example of the syntax anywhere.

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

    this.nameLength = ko.computed(function() {
        return this.name().length;
    }, this);
}

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

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

EDIT : From the accepted answer below I found this to work

<span data-bind='text: AdditionalProperty'>

The knockout code

var mapping = {
    create: function (options) {
        //customize at the root level.  
        var innerModel = ko.mapping.fromJS(options.data);

        innerModel.AdditionalProperty = 'Hello World';

        return innerModel;
    }
}

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

//use this as our model bindings
ko.applyBindings(viewModel);
like image 979
fluent Avatar asked Dec 29 '11 21:12

fluent


People also ask

What is knockout ViewModel JS?

A ViewModel can be any type of JavaScript variable. In Example 1-3, let's start with a simple JavaScript structure that contains a single property called name .

What is knockout mapping?

Knockout is designed to allow you to use arbitrary JavaScript objects as view models. As long as some of your view model's properties are observables, you can use KO to bind to them to your UI, and the UI will be updated automatically whenever the observable properties change.

What is KnockoutJS used for?

Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model.

How do we activate a knockout 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.


3 Answers

You need to use a create method on the mapping object itself like:

var mapping = {
  //customize at the root level.  
  create: function(options) {
     //first map the vm like normal
     var vm = ko.mapping.fromJS(options.data);

     //now manipulate the returned vm in any way that you like
     vm.someProperty = "test";

     vm.someComputed = ko.computed(function() {
          return vm.first() + " " + vm.last();
     });

     //return our vm that has been mapped and tweaked
     return vm;
  }
};
like image 109
RP Niemeyer Avatar answered Oct 06 '22 00:10

RP Niemeyer


Here is a continuation to this answer based on RP Niemeyer's solution

This answer here is based on the solution above and from his blog--Thanks for that! I thought I should add some details because it addresses when the array is not a first level object.

 var data = {
person: {
       children: [{ id: 1, firstName: 'Scot', lastName: 'Weise'}]
    }
};

var mapping = {
    'children': {
        create: function(options) {
            return (new (function() {
                // setup the computed binding for some reason I had
                //  to use ko.dependentObservable instead of ko.computed
                // or errors showed up.
                this.fullName = ko.dependentObservable(function() {
                    return this.firstName() + ' ' + this.lastName();
                    }, this);
                    ko.mapping.fromJS(options.data, { }, this);
                })(/* call the ctor here */));
            }
        }
    };

    // Create view model and map the data object to the model.
    var viewModel = ko.mapping.fromJS(data, {});

    // update the viewModel with the mapping object that contains 
    // some a computed property(s)
    viewModel.person = ko.mapping.fromJS(viewModel.person, mapping);
    ko.applyBindings(viewModel);

Note that the person is the first level object and children is a sub property of that person. The line viewModel.person = ko.mapping.fromJS(viewModel.person, mapping) wasn't intutive to me at first.

And here is a slight variation

The person object is an observable that is added or updated after it is originally created from the server json data.

var viewModel = {};
$(document).ready(function () {
    var person = getPerson();

    // selected person is added to the viewModel
    viewModel.selectedPerson = ko.observable(person);
    ko.applyBindings(viewModel);
});

function getPerson() { 
// you would probably pass this into the function as a parameter. 
var person =
    {
        name: 'jim',
        children: [{ id: 1, firstName: 'jane', lastName: 'bob'}]
    };

    var mapping = {
        'children': {
            create: function (options) {
                return (new (function () {
                    // setup the computed binding
                    this.fullName = ko.dependentObservable(function () {
                    return this.firstName() + ' ' + this.lastName();
                    }, this);
                    ko.mapping.fromJS(options.data, {}, this);
                })(/* call the ctor here */));
            }
        }
    };

    var result = ko.mapping.fromJS(person, mapping);
    return result;
}

Some binding code in the html

Ultimately, you'll need to put it to some use at some point like this:

<div data-bind="foreach:viewModel.selectedPerson().children">
    <span data-bind="text:fullName"></span>
</div>

Thank for your help! I couldn't have made it this far without your blog post.

like image 31
jwize Avatar answered Oct 06 '22 01:10

jwize


Note that to define additional computed observables on a child you will need to pass another set of mapping options

var mapping = {
  create: function(options) {
    //customize at the root level.  

    var mapping2 = {
      'children': {
        create: function(options) {
          //customize children also

        }
      }
    }

    //call ko.mapping.fromJS on the options.data as well with further customization
    ko.mapping.fromJS(options.data, mapping2, this);
  }
};
like image 21
Jason Avatar answered Oct 06 '22 00:10

Jason