自定义绑定中的正确依赖跟踪

时间:2015-09-20 21:42:57

标签: javascript knockout.js javascript-databinding

我想要实现的是以可视方式过滤由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依赖关系跟踪机制决定此绑定不依赖于我的视图模型中的任何可观察对象。
当过滤器可观察量(filterFromDatefilterDriver)的值发生变化时,update永远不会再被调用,整个过滤也不起作用。

如何改进此实现(或问题的整个方法),以便不要对过滤函数进行丑陋的调用,这至少会使函数等待undefined值作为参数?

1 个答案:

答案 0 :(得分:2)

您可以在visible上使用tr绑定,并使用$data作为参数将其绑定到函数调用的结果。下面的一个小演示。您选择的任何值都会从表格中过滤掉。

&#13;
&#13;
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;
&#13;
&#13;