2017-01-06

tobijibu

w2uiのグリッドでcolumsを動的生成し時にもrenderプロパティを使う方法

w2uiのグリッド(w2grid)でソートを使う際に、時間などのテキストをソートしたい場合に思うようにいかなかったのでその対策を書きます。

たとえば「一週間の稼働時間を一覧で表示する」としましょう。DBにはユーザー名と稼働時間として「分」のデータが入っているとします。

下記はDBから取得したデータを配列化したものです。

/**
 * データ
 * totalは「分」を表しています。
 * totalを表示上はhh:mm形式で表示し、裏では「分」のままで扱うことで、正しくソートさせます。
 */
$data = array(
    array('recid' => 1, 'name' => 'John Doe',           'total' =>  100,),
    array('recid' => 2, 'name' => 'Stuart Motzart',     'total' =>  120,),
    array('recid' => 3, 'name' => 'Jin Franson',        'total' =>   60,),
    array('recid' => 4, 'name' => 'Susan Ottie',        'total' =>    8,),
    array('recid' => 5, 'name' => 'Kelly Silver',       'total' =>  600,),
    array('recid' => 6, 'name' => 'Francis Gatos',      'total' =>   25,),
    array('recid' => 7, 'name' => 'Mark Welldo',        'total' => 1520,),
    array('recid' => 8, 'name' => 'Thomas Bahh',        'total' =>  660,),
    array('recid' => 9, 'name' => 'Sergei Rachmaninov', 'total' =>   99,),
);

PHPデータを取得・整形し、JSON形式でJSに渡すことは難しくないでしょう。配列をJSON形式に変換するだけであればjson_encode($data);で変換できます。

ただ、このままでは稼働時間が「時間(分)」の数値として表示されてしまいます。稼働時間の一覧なのでhh:mm形式で表示したいものです。ではPHPで配列を整形する時にhh:mm形式に変換して配列に格納すればよいでしょうか。しかし、そう簡単にはいきません。

結果を「時間(分)」でソートをしたい場合、hh:mm形式ではテキストとして扱われてしまい上手くソートすることが出来ず不都合です。正しいソートを実現するにはどのようにすればよいでしょうか。公式サイトで以下のような記述がありました。

I see your problem. Here is how I would do.
1. Date field should be in some sortable format (a) unix time, which is number of milliseconds from 1970 or (b) in the format YYY/MM/DD - then as a string it is sortable. If your date in any of this formats it will be sortable.
2. You need a user render property for the column. In fact in current master, there is one.

In current master you can find example in test/grid.html. It uses first approach, plus in addition it has search fields that allow you to search by date.
引用 - http://w2ui.com/ (http://disq.us/p/nll7qa)

ソート用のデータを持ちつつ、表示用はrenderプロパティを使って表示せよ。とのことです。

renderプロパティはパラメータを2種類の方法で渡すことができます。

  • 予め決められたフォーマットを渡す
  • 値を整形するためのfunctionを指定する

つまり、PHPでは「時間(分)」を整形せず、renderプロパティにfunctionとして「時間(分)」をhh:mm形式に変換する処理を入れればよさそうです。

/**
 * 列データ
 * 列ごとの設定です。この配列をJSONに変換してJSに渡します。
 */
$columns = array(
    array('field' => 'name', 'caption' => 'Name', 'size' => '50%', 'sortable' => true,),
    array(
        'field' => 'total',  'caption' => 'total',  'size' => '50%', 'sortable' => true,
        /**
         * 表示用の計算
         * 「分」からhh:mmに変換
         */
        'render' => 'function (record) {
            var total = record.total,
                h, m;
            h = Math.floor(total / 60);
            m = total % 60;
            if (String(m).length == 1) {
                m = "0" + String(m);
            }
            return h + ":" + m;
        }',
    ),
);

ここで問題が発生します。columnsをPHPで生成している場合、この方法はできません。なぜなら、PHPでrenderプロパティを含めたJSONを生成すると、renderプロパティの処理(JSのfunction)が文字列として渡されてしまうからです。そこでFunction.call()を使います。

DATA = {
  columns : <?php echo json_encode($columns)?>,
  data    : <?php echo json_encode($data)?>,
}
//total列のrenderをfunctionとして書き換えます。
DATA.columns[1].render = Function.call(null, "return " + DATA.columns[1].render)();

DATA.columnsDATA.dataの取得は説明用に1つのPHPファイルで書く為に単純化しています。ご利用のフレームワークや、APIに応じて書き換えることをおすすめします。

renderプロパティの中身を文字列からJSに書き換えた状態でw2gridを生成すれば、表示も正しく、ソートも出来るということになります。

$('#myGrid').w2grid({
    name   : 'myGrid',
    columns: DATA.columns,
    records: DATA.data,
});

今回説明で利用したソースはこちらにあります。