Migaro. 技術Tips

                       

ミガロ. 製品の技術情報
IBMiの活用に役立つ情報を掲載!


グリッドの集計行セルに高度なロジックを追加する

(※このトピックスは、Valence開発元(米CNX社)のブログ記事を翻訳・再編集したものとなります。原文記事は、コチラとなります。)

Valence の Nitro App Builder で作成される業務アプリにおいて、圧倒的に人気のあるウィジェットが「グリッド(Grid)」です。グリッドは、データセルの表示方法に関して多くのオプションがあり、非常に柔軟にカスタマイズできます。
テキストの配置や通貨/日付のレンダリング、セルの条件付きカラー表示といったシンプルな表示機能から、画像表示、数式の適用、基本的な JavaScript を使った条件分岐(if/then ロジック)まで、IBM i システム上のほぼすべてのデータをわかりやすい形で可視化できます。また、数値データについては、チェックボックスを1つオンにするだけで簡単に集計を行うことも可能です。

さらに柔軟な設定を可能にする新機能として、サマリ行のセルを他のセルの内容に基づいてカスタマイズできる機能が、最新の Valence 6.1 ビルドで追加されました。これにより、グリッドの下部に表示されるサマリ行の任意のセルが、他のサマリ列の値を参照して独自の計算結果を表示することが可能になります。つまり、合計(total)や平均(average)、最小/最大(min/max)などの標準的な集計関数にとらわれず、より柔軟な表示が実現できるということです。

本題である「サマリ行セルへのカスタムフォーマットの適用方法」に入る前に、まずは通常の行セルに対するカスタムレンダリングの設定方法をおさらいしておきましょう。
グリッドウィジェットの設定画面で <> のアイコンをクリックすると、カスタムレンダラーの設定パネルにアクセスできます。

これをクリックすると、各行の該当列に適用される JavaScript 関数を設定するためのウィンドウが表示されます。
この関数は、ビルド 6.1.20230224 以降、セルの表示内容を決定する際に利用できる 6つのパラメータ を含んでいます。
これらのパラメータを使うことで、セルに表示する値を柔軟に制御できるようになります。

  • v:該当セルに表示される元の値で、元となるデータソースから取得されます。
  • rec:その行全体のレコードオブジェクト。他の列の値を参照したい場合に使用できます。
  • filters:グリッドに適用されているフィルターの情報を保持しています。ユーザーが指定したフィルター値を取得するのに便利です。
  • column:現在処理中の列の定義情報への参照です。
  • grid:グリッド全体のオブジェクトを参照します。
  • summary(※6.1.20230224以降の新機能):サマリ行オブジェクトへの参照。他のサマリセルの値を取得したいときに使用します。なお、このパラメータはサマリ行だけでなく、通常の行でも使用可能です。

カスタムレンダラー関数は、最終的に表示したい値を return で返す必要があります(例:return v;)。
セルの中では HTML のフォーマットが使えるため、<br> を使って改行を入れたり、<i>(斜体)、<b>(太字)、<u>(下線)などのタグで文字を装飾することができます。
また、セルにマウスカーソルを合わせたときに表示される補足テキスト(ツールチップ)を追加したい場合は、戻り値の中に data-qtip 属性を持つ <div> タグを入れることで実現できます。

rec パラメータは、同じデータソース内の別の列から値を取り出すときに便利です。グリッド上で表示されていない列や、非表示にしている列の値でも取得できます。
例では、rec.get(‘CSTATE’) と rec.get(‘CCOUNTRY’) を使って州と国の値を取り出し、それを v に追加しています。これにより、たとえば州の値が空でない場合は、City 列に国の名前を別の行として表示することができます。

まれなケースとして、ユーザーが指定したフィルターの値に基づいてセルの内容を変更したい場合があります。
このような状況では、filters.getValue(‘フィールド名’) を使ってフィルターの入力値を取得することができます。ここでの「フィールド名」は、フィルターが適用されている列の名前です。

さらに高度なケースでは、columngrid パラメータを使って、Ext JS のコアとなるカラムオブジェクトやグリッドオブジェクトにアクセスする必要が出てくるかもしれません。
実際に一部のサイトでは、この方法を使って行全体の見た目を変更したり、列の見出しを動的に変更したりしています。
ただし、これはかなり複雑な JavaScript の知識が必要になるため、ここでは詳しく触れません。もしこの件について疑問があれば、Valence フォーラムに質問を投稿することをおすすめします。

最後に、summary パラメータについて説明します。これは rec パラメータと似ていますが、サマリ行のセルの値を参照するためのものです。レンダラーがサマリ行に対して呼び出されているかどうかは、summary.isRow が true かどうかで判断できます(これについては後ほど具体例で示します)。
また、集計が設定されていない列に対しても、「Apply on summary row(サマリ行に適用)」というチェックボックスをオンにすることで、強制的にサマリセルを表示することが可能です。
このチェックボックスは、すでに集計設定がされている列には表示されません。

この考え方を実際にどう活かすかを、以下の架空のデータソースに対しての SQL を紹介します。このデータソースは、顧客ごとに注文内容を集計しており、次の3つの計算項目の列を含んでいます。

  • 注文1件あたりの平均金額(合計金額 ÷ 注文件数)
  • 顧客ごとの注文金額の合計
  • 顧客ごとの注文件数

このグリッドでは、これらの算出された値を表示できるだけでなく、設定タブ内の電卓アイコンをクリックして、サマリ関数を選択することで下部に集計行を表示することも可能です。
たとえば、「注文金額」や「注文件数」の列には合計(Total)や合算(Sum)を表示し、「平均注文金額」の列には各行の平均値の平均(Average of Averages)を表示させることができます。

このように「平均注文金額(AVG_ORDCAL)」列に対してサマリ行で平均を表示すると、各行(顧客)を均等に扱った平均値になってしまい、全体の正確な注文単価の平均とは異なる結果になってしまいます。
この問題を解決するために活用できるのが、サマリセルの内容を上書きするカスタムレンダラーの機能です。
全注文に対する真の平均注文金額を算出するには、注文金額の合計 ÷ 注文件数の合計という計算を行う必要があります。
この計算を実現するために、「AVG_ORDCAL」列にカスタムレンダラーを設定し、サマリ行に対して次のような処理を行うようにします。

つまり本質的には、サマリ値に対してその場で計算を行っているということになります(ここで使っているロジックは、summary.isRow の値をチェックすることで、それがサマリセル内であるかどうかを判断しています)。
また、このカスタム JavaScript コードは、標準の歯車アイコンから設定できるレンダラーの代わりに呼び出されるため、セルの表示内容をすべてこのコードで制御する必要があります。
そのため、表示のフォーマットも自分で実装する必要があります。
この例では、計算結果を四捨五入して整数にし、カンマ区切りとドル記号($)を付けて表示しています。
その結果、平均注文金額のサマリ行には、他のサマリ列の値を元に再計算された別の平均値が表示されるようになります。

ちなみに、今回のように JavaScript で数値や文字列を操作する方法については、インターネット上にたくさんの例があり、たとえば「JavaScript format number」などのキーワードで検索すれば、すぐに見つけることができます。
前にも触れたように、summary パラメータを使ったもうひとつの便利な使い方として、サマリ行の値を使って通常の行の中で計算を行うという方法があります。
たとえば、ある顧客の注文件数が全体の中でどれくらいの割合を占めているかを表示したい場合、サマリ行から全体の注文件数を取得し、それをもとに各行で割合を計算し、注文件数の列にそのパーセンテージを追加で表示することができます。

結果として、通常の行セルは次のようになります。