2021年1月4日月曜日

対話グリッドの列の計算方法について

 Stefan Dobreさんによるこちらの記事 How to dynamically compute Interactive Grid Columns in #orclAPEX 20.2 の紹介です。

記事に記載されている実装を行うことで、理解してみました。色々と応用ができますし、同様の処理を大量の動的アクションで実装していた方は、ずっと分かりやすく書き換えることができるでしょう。

この機能は、Oracle APEXを20.2以降よりサポートされています。

以下が確認作業の順番です。

  1. 表とアプリケーションの作成
  2. CSVファイルのロード
  3. 計算によって導出される列を、対話グリッドのSQLに追加
  4. 対話グリッド上で、列の計算を行うコードを追加
1から2までは準備作業になります。Oracle APEXによるアプリケーション作成について経験のある方にとっては、冗長かもしれません。

表とアプリケーションの作成


サンプルの受注一覧のデータより、商品(PRODUCT_NAME)の価格(PRICE)と数量(QUANTITY)より、売り上げ(REVENUE)を計算します。売り上げは

REVENUE = PRICE * QUANTITY

として計算するため、表の列には含みません。

クイックSQLに与える設定は、以下とします。
# prefix: dmt
orders
  product_name vc255 /nn
  price num
  quantity num
SQLワークショップよりクイックSQLを開き、左側に上記のモデルを入力します。

SQLの生成SQLスクリプトを保存レビューおよび実行を順次実行します。


SQLスクリプトが開きます。DDLの変更は不要なので、そのまま実行します。確認画面が開くので、即時実行をクリックします。


DMT_ORDERSが作成されます。続けてアプリケーション作成を実行します。


アプリケーション作成ウィザードが起動します。

作成するアプリケーションの名前対話グリッドの計算とします。デフォルトで追加されているページをすべて削除し(編集をクリックして削除を実行)、代わりにページの追加をクリックして対話グリッドを追加します。


対話グリッドのページ名対話グリッドの計算とします。表またはビュー編集を許可を選択し、表またはビューとして表DMT_ORDERSを選択します。

ページの追加をクリックします。


以上の設定を行い、アプリケーションの作成を実行します。

アプリケーションが作成されます。今回の作業はページ番号1の対話グリッドの計算のページに対して実施します。



CSVファイルのロード



ロードするCSVファイルを、以下よりダウンロードします。

https://apex.oracle.com/pls/apex/japancommunity/r/simcontents/download?id=SampleOrders.csv

次にSQLワークショップユーティリティより、データ・ワークショップを開きます。データのロードをクリックして開きます。


ファイルのアップロード(デフォルトで選択されています)より、ファイルの選択をクリックして、先ほどダウンロードしたSampleOrders.csvを選択します。


ロード先既存の表としてクイックSQLで作成した表DMT_ORDERSファイル・エンコーディング日本語(Shift JIS)を指定します。最初の行にヘッダーが含まれるチェックする、列デリミタ,(カンマ)囲み文字"(ダブルクォート)は、デフォルトのままとします。

データのロードを行う前に、構成をクリックします。


受注番号のマップ先としてID(Number)製品PRODUCT_NAME(Varchar2)単価PRICE(Number)数量QUANTITY(Number)を指定します。単価と数量にはグループ・セパレータとして,(カンマ)小数点文字として.(ピリオド)を指定します。私がテストしたときは、小数点文字のデフォルトが,(カンマ)になっていたので、設定変更は必須です。

表DMT_ORDERSに含まれる4つの列をマップして、変更の保存を行います。


構成が保存されたので、データのロードを実行します。


103行のデータがロードされていれば、ロードは成功です。続けてアプリケーションの作成を実行します。



列REVENUEをSQLに追加



対話グリッドのページをページ・デザイナで開き、対話グリッドのリージョンを左ペインより選択します。ソースタイプSQL問合せに切り替え、SQL問合せを以下の列REVENUEを追加したものに更新します。
select ID,
       PRODUCT_NAME,
       PRICE,
       QUANTITY,
       NVL(PRICE,0) * NVL(QUANTITY,0) as REVENUE
from DMT_ORDERS

REVENUEが追加されます。元記事によると、今回のソリューションは編集可能な列にのみ適用可能なので、タイプを表示のみに設定することは不可です。しかし、列REVENUEは計算された結果でありユーザーからの入力は禁止したいので、外観のCSSクラスis-readonlyを指定します。また、データベースのアップデート操作に含まれないよう問合せのみONにします。


変更を保存して、ページを実行します。


この状態で列Price、Quantityを変更して、保存をクリックすると列Revenueに計算結果が反映されることが確認できます。

次に、保存をクリックせず、PriceやQuantityを変更するとすぐにRevenueが更新される実装を行います。


対話グリッド上の列の計算



Oracle APEX 20.2から利用可能になったcalcValueとdependsOnの機能を使用します。

列REVENUEのプロパティである詳細列初期化JavaScriptファンクションに以下を記述します。


dependsOnとして、この列REVENUEが依存している列を指定します。REVENUEはPRICEとQUANTITYの積なので、dependsOnはPRICEとQUANTITYになります。

dependsOnで指定された列、PRICEまたはQUANTITYが変更されたときに呼び出されるファンクションをcalcValueとして記述します。戻り値が列REVENUEの値になります。

以上のコードを設定して、期待した動作になるか確認してみましょう。列REVENUEを計算しているJavaScriptのコードの部分については、理解が難しいところはないかと思います。


おまけ:書式マスクについて


数値列である列PRICEとQUANTITYに以下のような書式マスクの設定がされていると、3桁ごとに区切り文字として,(カンマ)が含まれます。

999G999G999G999G999G999G999G999G999G990

このままではparseInt()で適切な整数として変換されないので、replaceAll(",","")でカンマを取り除いています。

列PRICEやQUANTITYを変更すると列REVENUEが更新されますが、この計算結果には,(カンマ)が含まれません。一旦、対話グリッドを保存すると書式マスクが適用され、,(カンマ)が表示されます。

対話グリッドの計算結果としても,(カンマ)を含めるには、Intl.NumberFormatを使って以下のように記述することで対応できます。

以上で、対話グリッドの列の計算方法の紹介は終了です。

今回作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/calcvalue-interactive-grids.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。