我想要实现的是以可视方式过滤由foreach
绑定生成的表行,其方式是过滤掉的行的tr
元素将会被隐藏而不是从DOM中删除
当用户更改过滤条件时,此方法可显着提高呈现性能。这就是为什么我不希望将foreach
绑定到计算的可观察数组,该数组根据过滤条件进行更新。
我希望这个解决方案是一个随时可用的构建块,我可以在项目的其他地方使用。
据我熟悉Knockout,最好的方法是实现自定义绑定。
我打算使用这种绑定的方式是这样的:
<tbody data-bind="foreach: unfilteredItems, visibilityFilter: itemsFilter">
<tr>
...
</tr>
</tbody>
其中itemsFilter
是一个返回boolean
的函数,具体取决于当前行是否可见,如下所示:
self.itemsFilter = function (item) {
var filterFromDate = filterFromDate(), // Observable
filterDriver = self.filterDriver(); // Observable too
return item && item.Date >= filterFromDate && (!filterDriver || filterDriver === item.DriverKey);
};
这是我到目前为止的绑定实现:
/*
* Works in conjunction with the 'foreach' binding and allows to perform fast filtering of generated DOM nodes by
* hiding\showing them rather than inserting\removing DOM nodes.
*/
ko.bindingHandlers.visibilityFilter = {
// Ugly thing starts here
init: function (elem, valueAccessor) {
var predicate = ko.utils.unwrapObservable(valueAccessor());
predicate();
},
// Ugly thing ends
update: function (elem, valueAccessor) {
var predicate = ko.utils.unwrapObservable(valueAccessor()),
child = ko.virtualElements.firstChild(elem),
visibleUpdater = ko.bindingHandlers.visible.update,
isVisible,
childData,
trueVaueAccessor = function () { return true; },
falseVaueAccessor = function () { return false; };
while (child) {
if (child.nodeType === Node.ELEMENT_NODE) {
childData = ko.dataFor(child);
if (childData) {
isVisible = predicate(childData, child);
visibleUpdater(child, isVisible ? trueVaueAccessor : falseVaueAccessor);
}
}
child = ko.virtualElements.nextSibling(child);
}
}
};
ko.virtualElements.allowedBindings.visibilityFilter = true;
你是否看到那个带有谓词调用的丑陋init
部分而没有将对象传递给它?
如果没有这个,如果第一次Knockout调用foreach
方法时update
绑定没有生成行,则不会调用itemsFilter
过滤函数。
因此,不会读取任何可观察对象,并且KO依赖关系跟踪机制决定此绑定不依赖于我的视图模型中的任何可观察对象。
当过滤器可观察量(filterFromDate
和filterDriver
)的值发生变化时,update
永远不会再被调用,整个过滤也不起作用。
如何改进此实现(或问题的整个方法),以便不要对过滤函数进行丑陋的调用,这至少会使函数等待undefined
值作为参数?
答案 0 :(得分:2)
您可以在visible
上使用tr
绑定,并使用$data
作为参数将其绑定到函数调用的结果。下面的一个小演示。您选择的任何值都会从表格中过滤掉。
var vm = {
rows: ko.observableArray(['One', 'Two', 'Three']),
selected: ko.observable('One'),
isVisible: function(row) {
return row !== vm.selected();
}
};
ko.applyBindings(vm);
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options:rows, value:selected"></select>
<table border="1" data-bind="foreach:rows">
<tr data-bind="visible:$parent.isVisible($data)">
<td data-bind="text:$data"></td>
</tr>
</table>
&#13;