I've got a table with rows generated by the foreach
binding.
The only difference from the 'usual' KO tables is that for dynamic filtering of rows I'm using the visible
binding on tr
elements which is bound to a function that uses values of some observable
s to decide whether current row should be visible:
<table>
<thead>
...
</thead>
<tbody data-bind="foreach: unfilteredItems">
<tr data-bind="visible: $root.rowFilter($data)">
...
</tr>
</tbody>
</table>
I'm using this approach because it's a way much better in terms of performance than traditional manipulation with a collection the foreach
is bound to and which results in constant inserting\removing of DOM nodes.
The only problem here is that there is no pure CSS solution for alternating the color of the rows. The tr
nodes stay in the DOM and :nth-child()
selector won't work properly when some tr
s aren't visible.
So, I'm forced to stick with the jQuery solution:
function stripeVisibleRows(tableElem) {
var $visibleRows = $(tableElem).find('tr:visible');
$visibleRows.filter(':odd').css('background-color', '#EEEAE7');
$visibleRows.filter(':even').css('background-color', '#DED7D1');
};
But how can I call this function in Knockout exactly after execution of the visible
bindings for all the rows when any of observables used by rowFilter
triggers updating the visible
bindings?
I've tried subscribe
ing to a fake computed
depending on the rowFilter
function:
self.rowFiltering = ko.computed(function () {
return self.rowFilter();
})
.subscribe(function () {
tableHelpers.stripeVisibleRows('.tbl');
});
, writing a custom binding like this one:
// Stripes visible tr elements.
// Must be applied to a table element.
// Must be bound to the same expression as used to change visibility of the tr elements.
ko.bindingHandlers.stripeVisibleRows = {
update: function (element, valueAccessor) {
// Read provided binding value to establish dependencies tracking
ko.utils.unwrapObservable(valueAccessor());
tableHelpers.stripeVisibleRows(element);
},
after: ['visible']
};
and its usage:
<table data-bind="stripeVisibleRows: $root.rowFilter()">
<thead>
...
</thead>
<tbody data-bind="foreach: unfilteredItems">
<tr data-bind="visible: $root.rowFilter($data)">
...
</tr>
</tbody>
</table>
but with both methods my jQuery striping function gets called before applying the visible
bindings and, therefore, doesn't work properly.
Could someone advice how to achieve what I'm trying to do?
Maybe I even need to change the whole approach to filtering and striping rows, but it should be without inserting\removing DOM nodes and as clean and reusable as possible.