AngularJSリストフィルター

多くのデータを繰り返し表示するときに便利なのがリストであり、そのリストの項目を後から操作できるのがリストフィルターである。この2つを組み合わせてデータを並べて表示する方法について説明する。

リストの繰り返し処理

データを扱うWebページでは、同じ形式の大量のデータをテーブルなどの形で表示することが多い。

このような場合は、「同じ表示を繰り返し実行する」という構造を持っている。つまり、データを「この形で書いていく」と指定しておけば、後はデータを渡すだけで、その一つ一つを同じ形式で繰り返して出力してくれる構造である。

AngularJSには「リスト」というものがある。これは配列などの形でまとめたものから順番に値を取り出して処理していく構造である。HTMLに属性を追加するだけで簡単に利用できる。

では例を作りながら説明する。まずスクリプトを作成し、script.jsというファイル名で保存する。

var myapp = angular.module('myapp',[]);
var helo = myapp.controller('HeloController',
    function(){
        this.count = 0;
        this.data = [
            {id:0,name:'no data',price:0,get:false},
            {id:1,name:'Android phone',price:7800,get:true},
            {id:2,name:'New iPhone',price:549020,get:false},
            {id:3,name:'windows phone',price:38765,get:true}
        ];
    }
);

ここではHeloControllerというコントローラに、dataという変数としてidnamepricegetなどの項目を持つデータを配列でまとめている。このデータをリスト形式で表示してみる。

ng-repeatの動作

次にHTMLを作成する。例は次のとおりである。

<!DOCTYPE html>
<html>
<head>
    <title>AngularJS Sample</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script src="script.js"></script>
    <style>
    body { color:gray; }
    h1 { font-size:18pt; font-weight:bold; }
    span.label { display:inline-block;width:50px; color:red; }
    input { width:100px; }
    .msg { font-size:14pt; font-weight:bold;color:gray; }
    th { color:#eee; background-color:#999; padding: 5px 10px;}
    td { color:#333; background-color:#ddd; padding: 5px 10px;}
    </style>
</head>
<body ng-app="myapp" ng-init="num=0">
    <h1>データ表示</h1>
    <div ng-controller="HeloController as ctl">

    <table>
    <tr><th>ID</th><th>NAME</th><th>PRICE</th><th>GET?</th></tr>
    <tr ng-repeat="obj in ctl.data">
        <td>{{obj.id}}</td>
        <td>{{obj.name}}</td>
        <td>{{obj.price | currency:"₩"}}</td>
        <td>{{obj.get}}</td>
    </tr>
    </table>
     
    </div>
</body>
</html>

このように書いてブラウザに表示してみる。dataのデータがテーブルとして表示される。

ここではテーブルを使ってデータを表示している。該当部分は次のとおりである。

<table >
  <tr><th>...省略...</th></tr>
  <tr ng-repeat="obj in ctl.data">
    <td>{{obj.id}}</td>
    <td>{{obj.name}}</td>
    <td>{{obj.price | currency:"₩"}}</td>
    <td>{{obj.get }}</td>
  </tr>
</table>

<table>には特別なことはない。重要なのは<tr>タグである。ここにリストによる繰り返しの属性、つまりディレクティブが書かれている。これは次のように書く。

ng-repeat="変数 in 配列"

このng-repeatは、用意された配列から値を取り出して変数に設定し、このng-repeatが書かれているタグを出力する。つまり、配列に保存されている値の数だけ、ng-repeatが書かれているタグが繰り返し出力される。<tr>タグ内の<td>タグを見ると、

<td>{{obj.id}}</td>

このように、配列から得た変数objオブジェクトのプロパティを指定して出力している。オブジェクトを配列にまとめておけば、このようにオブジェクト内の値も自由に扱える。

リストフィルター

複数のデータを繰り返し表示することは、リストで簡単にできることが分かった。このリストは単に配列の内容を順番に表示するだけではない。表示するときにさまざまなデータを操作できる。

たとえば、前の例で<tr>タグ部分を次のように変えてみる。

<tr ng-repeat = "obj in ctl.data
    | orderBy : '-price' ">

こうすると、データのprice値が大きいものから順番に並べ替えて表示する。

ここではobj in ctl.dataの後に|記号を付け、そこにorderBy : '-price'を設定している。これはリストに適用するフィルターである「リストフィルター」である。以前、テキストを表示するときに使うフィルターについて説明したが、リストフィルターはそのリスト版といえる。リストに表示される項目や順序などを操作できる。

では、繰り返し表示で使用できるフィルターについて説明する。

並び順の指定

orderBy : 項目名

前の例で使用したものである。これは指定された項目を使って配列を並べ替える。コロンの後に項目名を指定する。このとき、名前の前にマイナス記号を付けると逆順に並べ替える。たとえばpriceと指定するとpriceが小さい順になるが、-priceと指定すると大きい順に並ぶ。

最大項目数の指定

limitTo: 整数

配列の項目数を指定する。たとえばlimitTo:5と指定すると先頭から5個を取り出す。また、limitTo:-5のようにマイナス記号を付けて指定すると、最後の5個を取り出せる。

値の比較

filter: パターン

正規表現パターンを使って特定の項目だけを抽出するために使う。これはテキストフィルターでも使用した。リストの場合は、リストの項目がパターンと一致するものだけを取り出して処理できる。たとえばfilter:'ok'と指定すると、値がokの項目だけが表示される。

カスタムリストフィルター

標準で提供されるリストフィルターはそれほど多くない。それでも正規表現を使えるため、「パターンを書けばだいたい可能だ」と考えることはできる。ただし、正規表現のパターンをいろいろ指定するとなると、必ずしも使いやすいとはいえない。

また、単純な値ではなく複雑な構造のオブジェクトを配列にまとめている場合は、オブジェクトに応じて必要な値を取り出して確認するようなフィルターが必要になる。

このような場合は、フィルターを追加して使うこともできる。テキストフィルターだけでなく、リストフィルターも自分で作成できる。これは次のように作る。

コントローラ.filter(名前, 関数);

テキストフィルターと同じである。ただし用意する関数は少し異なる。次のようになる。

function() {
  return function(引数) {
    ... 引数の配列を操作する ...
    return 配列;
  }
}

関数はさらに関数をreturnするようになっている。この返される関数が、フィルターとして実行される処理である。

このフィルター用関数には引数が1つ用意される。ここにはデータの配列が渡される。この配列をもとに新しい配列を作り、それをreturnすると、その配列をもとにng-repeatの繰り返しが実行される。つまり、どう新しい配列を作るかを考えて関数を作ればよい。

リストフィルターの作成

では簡単なリストフィルターを作り、そのフィルターを使った例のスクリプトを作成してみよう。script.jsを次のように作成して保存する。

var myapp = angular.module('myapp',[]);
var helo = myapp.controller('HeloController',
    function() {
        this.count = 0;
        this.data = [
            { id:0, name:'no data',price:0,get:false },
            { id:1, name:'Android phone', price:7800, get:true },
            { id:2, name:'New iPhone', price:549020, get:true },
            { id:3, name:'windows phone', price:38765, get:true },
            { id:4, name:'firefox phone', price:14370, get:true },
            { id:5, name:'blackberry phone', price:-123, get:false },
        ];
        this.getData = function() {
            return this.data[this.count].id + ': ' + 
                this.data[this.count].name + ', ' + 
                this.data[this.count].price + '.';
        };
    }
);
 
// getをチェックし、trueの場合だけ返す。
helo.filter('getRepeat', 
    function() {
        return function(items) {
            var res = [];
            angular.forEach(items,function(item) {
                if (item.get)
                    res.push(item);
            });
            return res;
        };
    }
);

今回はHeloControllerコントローラにgetRepeatというリストフィルターを作成した。フィルターで返される関数部分を見ると、次のようになっている。

function (items) {
  var res = [];
  angular.forEach (items, function (item) {
    if (item.get)
        res.push (item);
  });
  return res;
};

ここでは引数として渡されたitemsから、getの値がtrueのものだけを配列にまとめて返す。配列の繰り返し処理を行うために、angularオブジェクトのforEachを使用している。

angular.forEach(配列, 関数);

このforEachは、第1引数の配列から順番に要素を取り出し、第2引数の関数を実行する。取り出した要素は関数に引数として渡される。関数ではこの引数を使って、配列のすべての要素に対して処理できる。

カスタムフィルターの利用

では、作成したgetRepeatフィルターを使ってみよう。HTMLファイルを次のように作成する。

<!DOCTYPE html>
<html>
<head>
    <title>AngularJS Sample</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script src="script.js"></script>
    <style>
    body { color:gray; }
    h1 { font-size:18pt; font-weight:bold; }
    span.label { display:inline-block;width:50px; color:red; }
    input { width:100px; }
    .msg { font-size:14pt; font-weight:bold;color:gray; }
    th { color:#eee; background-color:#999; padding: 5px 10px;}
    td { color:#333; background-color:#ddd; padding: 5px 10px;}
    </style>
</head>
<body ng-app="myapp" ng-init="num=0">
    <h1>データ表示</h1>
    <p>テキストを入力してください。</p>
    <div ng-controller="HeloController as ctl">
     
    <div class="input">
        <span class="label">検索:</span>
        <input type="text" ng-model="fstr">
    </div>
    <p class="msg">{{ctl.getData()} }</p>
    <hr/>
    
    <table>
    <tr><th>ID</th><th>NAME</th><th>PRICE</th><th>GET?</th></tr>
    <tr ng-repeat="obj in ctl.data | filter:fstr | orderBy : '-price' | getRepeat">
        <td>{{obj.id}}</td>
        <td>{{obj.name}}</td>
        <td>{{obj.price | currency:"₩"}}</td>
        <td>{{obj.get}}</td>
    </tr>
    </table>
     
    </div>
</body>
</html>

今回はgetRepeatフィルター以外に、テキストを使って検索するフィルターも追加されている。HTMLファイルを開くと、「GET?」の値がtrueのデータだけがテーブルに表示される。これがgetRepeatフィルターによる処理である。

表示を確認した後、「検索」フィールドに何かテキストを入力してみる。すると、そのテキストを含む項目だけが表示される。これはfilterフィルターを利用した処理である。filterを使うと簡単にデータを検索できる。

<tr>タグのng-repeat部分を見ると、次のように書かれている。

ng-repeat="obj in ctl.data | filter:fstr | orderBy : '-price' | getRepeat"
  • obj in ctl.data: ctl.dataの配列から順番に値を取り出してobjに代入する処理を繰り返す。
  • filter:fstr: テキストにfstrが含まれるかを確認する。
  • orderBy : ‘-price’: price値が大きい順に並べ替える。
  • getRepeat: get値がtrueのものだけを表示する。

このように4つの値をまとめている。最初のobj in ctl.dataで繰り返し処理を行い、その後の3つのリストフィルターで配列の内容を操作している。これにより「検索」「並べ替え」「getRepeat」のすべてが含まれる。

リストフィルターもテキストフィルターと同じように、作ってみると意外に簡単に作成できる。リストとリストフィルターは、データを表示する場合には必須機能といえる。ここでしっかり理解しておこう。