One possible solution would be to use the {deferEvaluation: true}
option. So your computed function will be only evaluated when the viewModel
is ready and your Total
property is actually used:
var mapping = {
'Collection': {
create: function(options) {
var model = ko.mapping.fromJS(options.data);
model.Total = ko.computed(function() {
var result = this.Price() * viewModel.Count();
return result;
}, model, {deferEvaluation: true});
return model;
}
}
};
Demo JSFiddle.
However with this approach you are tightly coupling your view model name with your mapping options. Because if you ever change var viewModel = ko.mapping.fromJS(json, mapping);
and name your viewModel
differently then you have to also update the mapping configuration.
Because there is no way to walk the "parent" chain in the mapping config probably for this scenario the mapping plugin is not the best solution...
Here is an alternative approach using handwritten viewmodels and passing down the "root" where is it needed:
var MainViewModel = function (json) {
ko.mapping.fromJS(json, { 'ignore': ["Foo"] }, this); //map the rest
this.Foo = new FooViewModel(json.Foo, this);
}
var FooViewModel = function (json, root) {
ko.mapping.fromJS(json, { 'ignore': ["Bar"] }, this); //map the rest
this.Bar = new BarViewModel(json.Bar, root);
}
var BarViewModel = function (json, root) {
ko.mapping.fromJS(json, { 'ignore': ["Collection"] }, this); //map the rest
this.Collection = ko.mapping.fromJS(json.Collection, {
create: function(options) {
var model = ko.mapping.fromJS(options.data);
model.Total = ko.computed(function() {
var result = this.Price() * root.Count();
return result;
}, model);
return model;
}});
}
Demo JSFiddle.