AjaxFallbackDefaultDataTableに関するメモ

Wicketの拡張ライブラリーであるwicket-extensionsにAjaxFallbackDefaultDataTableというクラスがあります。
んで、最近いろいろ調べなおす機会があったのでメモとして残しておこうかなぁと。
wicket、およびwicket-extensionsのバージョンは1.3.5です。

このクラスを使うと下記のようなことが簡単に出来ます。

  • Ajaxによる繰り返しデータの表示
  • Ajaxによるページング
  • 「最大xx件中x件表示」という項目の表示
  • Ajaxによる各カラムでのソート機能

各項目に関してはこのクラスを使わなくてもWicket単体のコンポーネントを使って組み合わせれば実現可能なのですが、「どーせこいつらっていつも一緒につかうじゃん?だったら一つにしちゃおうぜ!」的ノリでwicket-extensionsの中の人が作成したクラスだと勝手に思い込んでますw

んで、このクラスの使い方ですが、例えば下記のようになります(Countryクラスは省略してます)

    public CountryListPage() {
        //カラムのListを作成し、テーブルのカラムの数だけPropertyColumnをaddする
        List<AbstractColumn> columnList = new ArrayList<AbstractColumn>();
        columnList.add(new PropertyColumn(new Model("ID"),"id","countryId"));
        columnList.add(new PropertyColumn(new Model("Name"), "countryName"));
        columnList.add(new PropertyColumn(new Model("Lang"), "lang"));

        //AjaxFallbackDefaultDataTableのインスタンスを生成
        //上記カラムのリストを第2引数に、第3引数にはSortableDataProviderを継承したクラスのインスタンスを、
        //第4引数には1度に表示する件数を指定。
        AjaxFallbackDefaultDataTable table = 
                                new AjaxFallbackDefaultDataTable("table", 
                                                                   columnList, 
                                                                   new CompanyDataProvider(), 
                                                                   3);
        add(table);
    }

    private class CountryDataProvider extends SortableDataProvider {

        public CountryDataProvider() {
            //デフォルトのソート情報を設定
            setSort("id", true);
        }

        @Override
        public Iterator iterator(int first, int count) {
            //Serviceクラスか、もしくはDaoはDIコンテナ使ってインジェクト。
            //serachの戻り値はList<Country>
            return countryService.search(first, 
                                          count, 
                                          getSort().getProperty(),
                                          getSort().isAscending()).iterator();
        }

        @Override
        public int size() {
            return countryService.count();
        }

        @Override
        public IModel model(final Object object) {
            //objectにはSortableDataProvider#iteratorにて取得されたオブジェクトが順次渡される
            return new LoadableDetachableModel(){
                @Override
                protected Object load() {
                    return object;
                }
            };
        }
    }
}

んで、このCountryListPage.javaに対するCountryListPage.htmlはこちら

<html>
    <head>
        <title>CountryListPage</title>
    </head>
    <wicket:head>
        <style lang="text/css">
            table{
                width:80%;
                text-align:center;
            }
        </style>
    </wicket:head>
    <body>
        <br/><br/>
        <table wicket:id="table">[table]</table>
    </body>
</html>

Java側で書く事は

  • カラム情報を生成する
  • 合計サイズを取得する
  • 実際に表示される分だけのデータを取得する

これだけを書けば終了です。
HTMLではTABLEタグとwicket:idを書くだけです。
また、CSSはいろいろ変更可能です。


この子出来る子!


Wicket本家のサンプル画面にも使い方が載ってるのですがなんか今見れなくなってます・・・
なので上記を実行した結果の画面をキャプチャしてみました(デザインは何もいじってないのでショボイです・・ゴメンナサイ><)

それぞれのポイントを見ていくと、まずカラム情報生成の箇所

        //カラムのListを作成し、テーブルのカラムの数だけPropertyColumnをaddする
        List<AbstractColumn> columnList = new ArrayList<AbstractColumn>();
        columnList.add(new PropertyColumn(new Model("ID"),"id","countryId"));
        columnList.add(new PropertyColumn(new Model("Name"), "countryName"));
        columnList.add(new PropertyColumn(new Model("Lang"), "lang"));

ここでPropertyColumnのリストを生成しているわけですが、コンストラクタの第1引数に指定するModelクラスのコンストラクタの文字列が実際に動かした際のテーブルのカラム名として表示されます。また、PropertyColumnの引数2つのコンストラクタを呼び出した場合は第2引数に指定するのはSortableDataProvider#iteratorにて返される各オブジェクトのプロパティ名を記述します(今回だとCountryクラス)。使い方はPropertyModelと同じ感じです。PropertyColumnの引数3つのコンストラクタを呼び出した場合は第2引数がソート用のキー情報、第3引数がプロパティ名になります。この引数3つのコンストラクタを呼び出した場合は自動的にカラム名に対してリンクを張ってくれます(上記画像における「ID」の部分ですね)

次は表示情報取得箇所(CountryDataProvider#iterator

        @Override
        public Iterator iterator(int first, int count) {
            //serachの戻り値はList<Country>
            return countryService.search(first, 
                                          count, 
                                          getSort().getProperty(),
                                          getSort().isAscending()).iterator();
        }

基本的にIDataProvider#iteratorと使い方は一緒です。第1引数には表示したい件数の最初の数、第2引数にはそこから何件表示させるかという情報が渡されます(例えば20件目から30件目を表示される場合、firstには20が、countには10が渡されます)。
そしてIDataProvider#iteratorと違う部分はgetSort().getProperty()、getSort().isAscending()を呼び出している部分です。
これを呼び出すことで「どのカラムでのソートか、そして昇順か降順か」という情報を取得できるようになります。
getSort().getProperty()ではPropertyColumnの3つのコンストラクタで呼び出した場合の第2引数の文字列(今回の場合だとnew PropertyColumn(new Model("ID"),"id","countryId")の"id")が取得できます。getSort().isAscending()では昇順の場合はtrueが、そうでない場合はfalseがboolean型で返されます。SQLを実際に発行するDAOの部分ではこの2つの情報を元にSQLが構築されるようになるかと思います。

最後に忘れてはいけない箇所

        public CountryDataProvider() {
            //デフォルトのソート情報を設定
            setSort("id", true);
        }

SortableDataProviderを継承したクラスのコンストラクタ内にて初回アクセス時のデフォルトのソート情報を設定しておかないとSortableDataProvider#iterator内でgetSort()を呼び出してもNullが帰ってきます。Nullチェックをすれば問題ないですが、デフォ値を設定しておけばif文書かなくていいしソースも見やすいかなぁ〜と思ったり。

こんな感じでAjaxFallbackDefaultDataTableを使えば「Ajax」ということをまったく意識せずにAjaxバリバリの繰り返し情報表示処理が書けます。この子出来(ry
ただ、デメリットというか使用するのにためらうかもしれない点としてはListViewやDataViewと違って行毎に表示するという感じではなく列毎に表示する感じをうけること(詳しくは次回のエントリーで書きます)。あといろいろ勝手にやりすぎてしまってくれる事ww(いや、これはデメリットではないでしょうねw)それと、このエントリーを書いているときにこちらにて矢野さんがSeasarConfarenceにてお話をされた事のレポートが詳細に書かれているのですが、テーブルのソート用ヘッダをコンポーネントかされいるとか。AjaxFallbackDefaultDataTableだとソート情報は1つしか持てないし、DataViewとかと使い方が多少違うのでその点とかを考慮されたのかなぁと勝手に推測してます。そのあたりを実際に矢野さんに聞いてみたかった!というかSeasarConfarence行きたかった!!!・・・orz

次回はAjaxFallbackDefaultDataTableの拡張ポイントとかを書こうと思います。