web-dev-qa-db-ja.com

AngularJS「ng-filter」は、〜1000要素の配列で非常に遅い

シンプルな<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個のアイテムをフィルター処理しているためである可能性が高いことは承知していますが、他の場所ではこれに関する議論はありません。

フィルターのパフォーマンスを向上させる方法はありますか?

36
JVG

フィルターアプローチの主な問題は、変更が行われるたびにDOMが操作されることです。そのため、遅いのはフィルターではなく、結果です。別の方法は、次のようなものを使用することです。

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

繰り返される要素。

@OverZealousからいくつかのコードを貸すと、以下を使用して動作を比較できます。


Update:Angular v1.2にはtrack by構文。これはそのような問題にも役立ちます。要素に一意の属性がある場合は、次を使用できます。

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

どこ item.idは、すべてのアイテムにわたって一意である必要があります。 track by dom-elementsのみが削除され、最終的なリストには含まれなくなり、その他はrememberedになります。一方、track byリスト全体が毎回redrawnです。要するに:dom操作がずっと少ない=再描画が速い。

55
Yoshi

別の興味深い最適化は、特定の時間までモデルの変更を「トリガーしない」ことです。

これを検索入力フィールドに追加:ng-model-options = "{debounce:500}"

ユーザーが500ミリ秒の間に入力を停止すると、これによりフィルターがトリガーされます。

上記のフィドルを更新しました:

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

<input ng-model="searchFilter" type="text" ng-model-options="{debounce: 500}" />
21
Tyvain

表示しているコード(の一部)をシミュレートするフィドルを作成しました。

私のコンピューターでは、高速ですが、超高速ではありません。少し時間がかかりますが、チェックボックスで双方向にバインドされている過度に長いリストをフィルタリングしています。文字を入力するたびに、リスト全体をスキャンし、アイテムを削除(または追加)する必要があります。

これを解決する最善の方法は、 this StackOverflow answerに示すように、ある種の単純なページネーションを追加することだと思います。

ここでページネーションを含むように例を変更しました 。おそらく、単に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>
5
OverZealous

入力でキーを押すたびに、すべての監視式を実行する必要があり、コードを見ると多くの式があります。アイテム名が不変の場合、たとえば https://github.com/abourget/abourget-angular を使用すると、次のように記述できます。

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

また、キーストロークごとに実行するウォッチ式が1000個少なくなります。

さらに、入力で何らかのスロットルを使用して、最後のキーストロークから500ミリ秒後にフィルターがトリガーされるようにすることもできます。

4
Tadeusz Wójcik

「limitTo」フィルターを使用して、表示されるアイテムを制限することもできます。これにより、フィルタリングしているモデルに大量のアイテムを保持できますが、DOMのすべてのアイテムを表示しようとしているわけではないので、遅くなりません。

以下は、以前の回答に基づいて修正されたjsbinですが、limitToフィルターが適用されています。

http://jsbin.com/IhOcaKo/1

4
George Witte

解決策はありません:(

最後に私は[〜#〜] solved [〜#〜]この方法で問題を単純に:

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

試してみて解決してください... :)

0
X-Coder