我为<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件物品,但我没有在其他地方看到过这方面的讨论。
有没有办法从过滤器中获得更好的性能?
答案 0 :(得分: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操作=更快的重绘。
答案 1 :(得分:21)
另一个有趣的优化是在一定时间内“不触发”模型更改。
将此添加到您的搜索输入字段:ng-model-options =“{debounce:500}”
如果用户在500ms内停止输入,则会触发过滤器。
我更新了上面的小提琴:
<input ng-model="searchFilter" type="text" ng-model-options="{debounce: 500}" />
答案 2 :(得分:5)
I created a fiddle to simulate (part of) the code you are showing.
在我的计算机上,这是快速但不是超快,运行良好。它有点慢,但它过滤了一个过长的列表,它与复选框有双向绑定。每次键入字母时,都必须扫描整个列表并删除(或添加)项目。
我认为解决这个问题最好的办法是添加一些简单的分页,如this StackOverflow回答所示。
Here I've modified my example to include 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>
答案 3 :(得分:4)
每次按下输入键时,所有需要执行的表达式都需要执行,并查看代码中有很多。 如果项目名称是不可变的,您可以使用例如https://github.com/abourget/abourget-angular,它允许您编写:
<label>
<input type="checkbox" ng-model="selectedData[key][value]" />
<span set-text='value'></span>
</label>
每次按键时,你只需要执行1000个监视表达式。
此外,您可以对输入使用某种限制,以便在上次击键后500ms后触发过滤器。
答案 4 :(得分:4)
您还可以使用“limitTo”过滤器限制将显示的项目。这允许您在模型中仍然有大量要过滤掉的项目,但它不会那么慢,因为您没有尝试显示DOM中的所有项目。
这是基于早期答案的修改后的jsbin,但是应用了limitTo过滤器:
答案 5 :(得分:0)
没有解决方案适合我:(
最后我求助这个问题就这么简单了:
<li ng-repeat="value in val | filter: searchFilter | limitTo:200">
试试看并解决......:)