I am working through the mapping plugin example on the Knockoutjs website.
This is the example data.
Knockout JS Mapping Plugin
var data = {
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);
}
};
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;
}
};
Another example based on the examples provided by Jason and RP Niemeyer.
data
is what we get after an ajax query, on which we add two nested observables (viewModel.weekly.selectedWeek
and viewModel.monthly.selectedMonth
):
var data = {
"date": "2017-03-28",
"weekly": {
"weeks": [
{
"id": "201701",
"label": "Week 1"
},
{
"id": "201702",
"label": "Week 2"
}
]
},
"monthly": {
"months": [
{
"id": "201701",
"label": "01/2017"
},
{
"id": "201702",
"label": "02/2017"
}
]
}
}
var viewModelMapping = {
create: function (options) {
return (new (function () {
// viewModel root level
var mapping2 = {
'weekly': {
create: function (options) {
// viewModel.weekly
return new function () {
var model = ko.mapping.fromJS(options.data, {}, this);
model.selectedWeek = ko.observable();
return model;
};
}
},
'monthly': {
create: function (options) {
// viewModel.monthly
return new function () {
var model = ko.mapping.fromJS(options.data, {}, this);
model.selectedMonth = ko.observable();
return model;
};
}
}
};
ko.mapping.fromJS(options.data, mapping2, this);
})());
}
};
var viewModel = ko.mapping.fromJS(data, viewModelMapping);
ko.applyBindings(viewModel);