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);
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 .
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.
Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data 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.
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;
}
};
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.
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);
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With