Reputation: 4167
I'm building a very number-heavy app and one feature of the app is to be able to filter between the three results at the end. These are: Drugs, Disease & All (both).
I'm pretty new to Knockout so not totally sure what the best-practise way to approach the following situation would be.
I have the values filtering, my problem lies with computing the new values. The totals at the bottom have to reflect what data is currently visible in the table. Although these cell values (apart from totals) in the codepen below are static, they're actually dynamic in my app and they're a computed combination of a couple of values that are stored in sessionStorage in the app on previous pages. (This screen is a totals page).
Example:
self.total = ko.computed(function () {
return sessionStorage.getItem('1') * sessionStorage.getItem('2')
});
So to clarify; initially the computed totals at the bottom of the table are a total sum of numbers in the table cells that are multiplications of numbers from sessionStorage. The totals values then need to update every time the table is filtered to reflect the total of the visible data.
http://codepen.io/anon/pen/nlemE
I appreciate this may be complicated to get your head around and if I haven't explained it well enough, let me know and I'll clarify.
Upvotes: 2
Views: 206
Reputation: 49744
You want to use a Knockout Subscription on each of your observables.
self.r1c1().subscribe(function(newValue) {
// update the sum
});
Rather than add 20 subscriptions however, you might want to use a Knockout Observable Array to store/update your individual values.
var myObservableArray = ko.observableArray();
myObservableArray.push(<value for row 1>);
myObservableArray.push(<value for row 2>);
// etc
Then you could get by with a single subscription for each of your three columns.
self.myObservableArray.subscribe(function(newValue) {
// re-calculate the sum of the values
});
Upvotes: 0
Reputation: 575
Rather than use jQuery to hide/show the rows, it would be better to use an observableArray
to store your values in, and a computed function to filter that array every time the filter value changes.
Here's an updated CodePen showing how that might work: http://codepen.io/anon/pen/HAzcf?editors=101
Your data code would be an array of objects instead of r1c1, r2c1, etc. We also add a filter field so we don't have to hard code that into our HTML.
self.values = ko.observableArray([
{
c1: ko.observable(10),
c2: ko.observable(32),
c3: ko.observable(36),
filter: ko.observable('drugs')
},
{
c1: ko.observable(70),
c2: ko.observable(46),
c3: ko.observable(31),
filter: ko.observable('disease')
}
// etc..
]);
Our toggle function now simply updates an observable:
// toggle
self.filter = ko.observable('all');
self.toggleVis = function (data, event) {
self.filter(event.target.value);
};
The filtered values are updated every time the self.filter
observable is updated:
self.filteredValues = ko.computed(function() {
var filter = self.filter();
return ko.utils.arrayFilter(self.values(), function(item) {
return filter == 'all' || item.filter() == filter;
});
});
And our totals properties are updated whenever the filteredValues are changed:
self.total1 = ko.computed(function () {
var total = 0,
values = self.filteredValues();
for(var i = 0; i < values.length; i++) {
total += values[i].c1();
}
return total;
});
Your HTML table code also becomes simplified to simply iterate over the filtered observable array:
<table>
<tbody>
<!-- ko foreach: filteredValues -->
<tr class="row">
<td data-bind="text:c1"></td>
<td data-bind="text:c2"></td>
<td data-bind="text:c3"></td>
</tr>
<!-- /ko -->
<tr class="totals">
<td data-bind="text:total1"></td>
<td data-bind="text:total2"></td>
<td data-bind="text:total3"></td>
</tr>
</tbody>
</table>
Upvotes: 1