自定义绑定中的适当依赖跟踪 [英] Proper dependecy tracking in a custom binding

查看:140
本文介绍了自定义绑定中的适当依赖跟踪的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要实现的目的是直观地过滤由foreach绑定生成的表行,其方式是隐藏被过滤掉的行的tr个元素,而不是隐藏它们已从DOM中删除.
当用户更改过滤条件时,此方法可显着提高渲染性能.这就是为什么我不希望foreach绑定到计算的可观察数组,该数组根据过滤条件进行更新.
我希望该解决方案能够作为现成的构建基块,可以在项目的其他地方使用.

What I'm trying to achieve is to visually filter table rows generated by the foreach binding in a way that tr elements of the rows that are filtered out would be hidden instead of removed from the DOM.
This approach significantly improves rendering performance when a user changes filter conditions. This is why I don't want the foreach to be bound to a computed observable array which is updated depending on the filter condition.
I want this solution to be a ready-to-use building block that I can use in other places of the project.

据我所知,最好的方法是实现自定义绑定.

As far as I'm familiar with Knockout, the best way is to implement a custom binding.

我打算使用这种绑定的方式是这样的:

The way I intended to use this binding is something like this:

<tbody data-bind="foreach: unfilteredItems, visibilityFilter: itemsFilter">
    <tr>
    ...
    </tr>
</tbody>

其中,itemsFilter是根据当前行是否可见而返回boolean的函数,如下所示:

where itemsFilter is a function returning boolean depending of whether current row should be visible or not, like this:

    self.itemsFilter = function (item) {
        var filterFromDate = filterFromDate(), // Observable
            filterDriver = self.filterDriver(); // Observable too

        return item && item.Date >= filterFromDate && (!filterDriver || filterDriver === item.DriverKey);
    };

这是我到目前为止的绑定实现:

Here is the binding implementation that I have so far:

/*
 * 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部分没有传递对象吗?

Do you see that ugly init part with predicate invocation without passing an object to it?

否则,如果在第一次敲除update方法时foreach绑定没有生成任何行,则不会调用itemsFilter过滤器函数.
因此,将不会读取任何可观察对象,并且KO依赖性跟踪机制会确定此绑定不依赖于我的视图模型中的任何可观察对象.
并且当过滤器可观察值(filterFromDatefilterDriver)的值发生更改时,update将永远不会再次调用,并且整个过滤将无法正常工作.

Without this, if there is no rows generated by the foreach binding by the first time Knockout calls the update method, itemsFilter filter function wouldn't be called.
Hence, no observables would be read and KO dependency tracking mechanism decides that this binding doesn't depend on any observables in my view model.
And when values of the filter observables (filterFromDate and filterDriver) get changed, the update will never be called again and the whole filtering doesn't work.

如何改进此实现(或解决问题的整个方法),以免对过滤器函数进行难看的调用,至少使该函数等待一个undefined值,如下所示:一个paramenter?

How can I improve this implementation (or the whole approach to the problem) in order to not make that ugly call to the filter function which at least makes the function await an undefined value as a paramenter?

推荐答案

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

You can use a visible binding on the tr and bind it to the result of a function call using $data as the parameter. A little demo below. Whichever value you select is filtered out of the table.

var vm = {
  rows: ko.observableArray(['One', 'Two', 'Three']),
  selected: ko.observable('One'),
  isVisible: function(row) {
    return row !== vm.selected();
  }
};

ko.applyBindings(vm);

<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>

这篇关于自定义绑定中的适当依赖跟踪的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆