🅰️

ng-repeatでのフィルタをフックする

2020/09/25に公開

(2014/10/25 追記)この記事は半年以上前に執筆したものです。

--

とくに何もせずng-repeatに Filter を指定すれば検索ができてしまうのが AngularJS の魅力ですが、この処理の前後に独自の処理を挟みたいときはどうするか考えてみました。

Filter 処理をフックする方法

Controller の実装については省略しています。

var myApp = angular.module("myApp", ["ngRoute"]);

myApp.filter("myFilter", function ($filter) {
  return function (items, input) {
    /* Filter前の何らかの処理 */
    var result = $filter("filter")(items, input); // ここでネイティブのfilterを呼ぶ
    /* Filter後の何らかの処理 */
    return result;
  };
});
index.html
<input ng-model="search" />
<table>
  <tr ng-repeat="e in entities | myFilter:search">
    <td>{{e.name}}</td>
  </tr>
</table>

jsFiddle

これで OK。var result = $filter('filter')(items, input);がポイントなんですね。この前後でいろいろできます。データが多い時に逐一やるとかなり重いので対策は必要になってきます。<input>ng-change属性を与えて debounce するのも手です。

また、この引数の取り方を覚えておけば他の何らかのフィルターを自作することも可能です。

応用

DOM 反映後に処理をする場合

Filter のreturn result;行にて戻ったオブジェクトが検索結果として DOM に反映されるわけですが、このフック処理は「反映前」に行われます。反映後に更に処理を行いたい場合は$scope.$watch()を使うとよさそうです。jQuery やgetElementById()辺りはフック処理に書いても動かないので注意。

表示上の順序と$index が一致しない

これは画面上ではフィルタ済み、保持しているオブジェクトはフィルタ前と異なるからです。フック処理でフィルタ後のオブジェクトを保持すれば、その配列インデックスと見た目の上での順序が一致します。nextSiblingの使い勝手が微妙な中、何かと便利。

Discussion