38

<input>为 中的项目名称列表设置了一个简单的搜索过滤器AngularJS

我的列表如下所示:

var uniqueLists = {
    category1: ['item1', 'item2', 'item3' ... 'item180' ], // Real list contains ~180 items
    category2: ['itemA', 'itemB', 'itemC' ... 'itemZZZ' ], // Real list contains ~1080 items
    category3: ['otheritem1', 'otheritem2', 'otheritem3' ]  // Real list contains 6 items
  }

我在 Angular 中遍历这个列表,并在<ul>每个类别中打印出结果。

<div ng-repeat="(key,val) in uniqueLists">
    <form ng-model="uniqueLists[index][0]">
        <input ng-model="searchFilter" type="text" />
            <ul>
                <li ng-repeat="value in val | filter: searchFilter">
                    <label>
                         <input type="checkbox" ng-model="selectedData[key][value]" />
                        {{value}}
                    </label>
                </li>
            </ul>
    </form>
</div>

为清楚起见, selectedData 如下所示:

var selectedData = {category1: [item1:true], category2: [], category3: []); // if 'item1's checkbox is checked.

这个列表工作得很好,虽然filter它相当滞后,即使在我相当快的计算机上也是如此。在输入中键入一个字母需要 1-2 秒来更新列表。

我知道这很可能是因为我一次过滤了大约 1000 个项目,但我没有在其他地方看到任何关于此的讨论。

有没有办法让过滤器获得更好的性能?

4

6 回答 6

55

过滤器方法的主要问题是,在每次更改时,dom 都会被操纵,所以不是过滤器变慢而是后果。另一种方法是使用类似的东西:

ng-show="([item] | filter:searchFilter).length > 0"

在重复的元素上。

从@OverZealous 借一些代码,您可以使用以下内容来比较行为:


更新:Angular v1.2 出现了track by语法。这也有助于解决此类问题。如果元素有一些独特的属性,可以使用:

ng-repeat="item in items | filter:searchFilter track by item.id"

item.id在所有项目中,哪里必须是唯一的。track by只有那些不再在最终列表中的 dom 元素将被删除,其他的将被记住。而没有track by整个列表每次都会重绘。简而言之:更少的 dom 操作 = 更快的重绘。

于 2013-07-31T13:23:01.583 回答
21

另一个有趣的优化是在特定时间之前“不触发”模型更改。

将此添加到您的搜索输入字段:ng-model-options="{debounce: 500}"

如果用户在 500 毫秒内停止输入,这将触发过滤器。

我更新了上面的小提琴:

http://jsfiddle.net/CXBN4/14/

<input ng-model="searchFilter" type="text" ng-model-options="{debounce: 500}" />
于 2015-01-29T23:45:03.540 回答
5

我创建了一个小提琴来模拟(部分)您正在显示的代码。

在我的电脑上,速度很快但不是超快,运行良好。它有点慢,但它过滤了一个过长的列表,该列表与复选框具有双向绑定。每次键入字母时,都必须扫描整个列表并删除(或添加)项目。

我认为解决此问题的最佳选择是添加某种简单的分页,如StackOverflow答案所示。

在这里,我修改了我的示例以包含 pagination。您可能希望投资于一个比Next / Previous更好的解决方案,但这向您展示了如果您不一次显示所有内容,结果将如何非常快。它仍然搜索整个列表,但呈现的列表受到更多限制。

补充:

将分页信息添加到控制器中的范围:

$scope.currentPage = 0;
$scope.pageSize = 20;
$scope.numberOfPages = function () {
    return Math.ceil($scope.items.length / $scope.pageSize);
}

创建一个新过滤器以从特定页面开始:

app.filter('startFrom', function () {
    return function (input, start, pageSize) {
        start = +start; //parse to int
        pageSize = +pageSize;
        while (start > input.length) {
            start -= pageSize;
        }
        if (start < 0) {
            start = 0;
        }
        return input.slice(start);
    };
});

向视图添加过滤器以限制列表:

<li ng-repeat="value in items | filter:searchFilter |
        startFrom:currentPage*pageSize:pageSize | limitTo:pageSize">

在页面中添加分页按钮:

    <div>
        <button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">Previous</button> {{ currentPage+1 }}/{{ numberOfPages() }}
        <button ng-disabled="currentPage >= items.length/pageSize - 1" ng-click="currentPage=currentPage+1">Next</button>
    </div>
于 2013-07-31T12:56:46.580 回答
4

每次按下输入键时,都需要执行所有监视表达式,并查看您的代码,您有很多。如果项目名称是不可变的,您可以使用例如https://github.com/abourget/abourget-angular它允许您编写:

<label>
     <input type="checkbox" ng-model="selectedData[key][value]" />
     <span set-text='value'></span>
</label>

每次击键时要执行的监视表达式减少了 1000 个。

此外,您可以对输入使用某种限制,以便在最后一次击键后 500 毫秒后过滤触发器。

于 2013-07-31T12:53:38.677 回答
4

您还可以使用“limitTo”过滤器限制将显示的项目。这允许您在模型中仍然有大量要过滤的项目,但它不会那么慢,因为您没有尝试显示 DOM 中的所有项目。

这是基于早期答案的修改后的 jsbin,但应用了 limitTo 过滤器:

http://jsbin.com/IhOcaKo/1

于 2013-10-14T19:25:44.090 回答
1

没有解决方案适合我:(

最后我以这种方式简单地解决了这个问题:

<li ng-repeat="value in val | filter: searchFilter | limitTo:200">

试一试并解决... :)

于 2017-06-21T07:17:41.163 回答