Reputation: 123
I need to have a computed observable for each element of an array. The resolution of this computed observable needs to rely on other properties existing in the context of each array element.
Please check the following example case:
This is the result of a KnockoutJS foreach binding to a nested array, having a group = category, and items nested in each category.
In my scenario, and initial array has "plain" properties in its elements, as if retrieved from a backend, to later be mapped into another array with the same structure, but with the quantity property deliberately set to a knockout observable. In this mapping process where I set quantity to be a knockout observable, I also need to set another computed observable property to represent the item total (price property x quantity property). This item total computed observable has to happen for each element of the array, and has to depend on the content of price and quantity property in the context of each array element.
Please check the following screen shot, imagine a bar menu where quantity is entered on the input tag bound to observable array elements, and upon changing each quantity, a computed observable bound to a td tag should reflect the item total calculation.
Please also check the JS Fiddle for this
As you will notice in the JS Fiddle, this part in the array mapping function to implement the totalComputed property is obviously wrong, it does not work.
totalComputed: ko.computed(function() {
return element.items[indexInArrayItems].quantity * element.items[indexInArrayItems].price;
})
Can you help, preferrably making my JS Fiddle work?
Upvotes: 1
Views: 1109
Reputation: 3644
You need to take advantage of knockout's power by creating observable variables and observableArray then have dependencies in your computed function in order to have your computed gets updated automatically every time any of its dependencies change.
Here is based on your JS Fiddle example : https://jsfiddle.net/rnhkv840/22/
var MainViewModel = function(){
var self = this;
self.dataSource = ko.observableArray([]);
//probably inside your ajax success
self.dataSource($.map(dataFromBackend, function (item) {
return new CategoryViewModel(item);
}));
}
var CategoryViewModel = function(data){
var self = this;
self.Items = ko.observableArray($.map(data.items, function (item) {
return new ItemViewModel(item);
}));
self.category = ko.observable(data.category);
}
var ItemViewModel = function(data){
var self = this;
self.description = ko.observable(data.description);
self.price = ko.observable(data.price);
self.quantity = ko.observable(data.quantity);
self.totalComputed = ko.computed(function () {
return self.price() * self.quantity();
});
}
var vm = new MainViewModel();
ko.applyBindings(vm);
Upvotes: 2