2021年9月1日水曜日

対話グリッドの列の文字の色を条件によって変更する - JavaScriptによる実装

 テンプレート・ディレクティブを使った設定ではなく、JavaScriptで単一行ビューの項目の色を変更する処理を実装してみました。部門番号(DEPTNO)に依存して従業員名(ENAME)の色を変更します。こちらの記事で作成したアプリケーションに機能を追加しています。

実装はしてみましたが、テンプレート・ディレクティブを使った方が実装としては良いでしょう。JavaScriptによるカスタム実装の場合、現状で年に2回発生するOracle APEXのアップデートの際に、毎回処理に影響がないことを確認しないといけません。

そういった点は置いておいて、せっかく調べたので純粋に技術的な観点で実装方法を紹介します。


動的アクションによる実装


単一行ビューに切り替わる場合、大抵は行が選択されています。なので、選択の変更タイミング動的アクションを実行します。

最初に対話グリッドに静的IDを設定します。静的IDはempとします。


対話グリッドEMP動的アクションの作成を行います。識別名前行の選択とします。タイミングイベント選択の変更[対話グリッド]選択タイプリージョンリージョンEMPとします。


TRUEアクションJavaScriptコードの実行とします。設定のコードには以下を記述します。

let view =
apex.region("emp").widget().interactiveGrid("getCurrentView");
if (view.singleRowMode) {
let model = this.data.model;
let record = this.data.selectedRecords[0];
let deptno = model.getValue(record, "DEPTNO");
let elem = this.triggeringElement;
let enameElems = elem.getElementsByClassName("u-bold");
if (enameElems.length == 1) {
switch(deptno) {
case '10':
if (enameElems[0].classList.contains("u-success-text")) {
enameElems[0].classList.remove("u-success-text");
}
if (enameElems[0].classList.contains("u-warning-text")) {
enameElems[0].classList.remove("u-warning-text");
}
if (! enameElems[0].classList.contains("u-danger-text")) {
enameElems[0].classList.add("u-danger-text");
}
break;
case '20':
if (enameElems[0].classList.contains("u-success-text")) {
enameElems[0].classList.remove("u-success-text");
}
if (enameElems[0].classList.contains("u-danger-text")) {
enameElems[0].classList.remove("u-danger-text");
}
if (! enameElems[0].classList.contains("u-warning-text")) {
enameElems[0].classList.add("u-warning-text");
}
break;
case '30':
if (enameElems[0].classList.contains("u-warning-text")) {
enameElems[0].classList.remove("u-warning-text");
}
if (enameElems[0].classList.contains("u-danger-text")) {
enameElems[0].classList.remove("u-danger-text");
}
if (! enameElems[0].classList.contains("u-success-text")) {
enameElems[0].classList.add("u-success-text");
}
break;
}
}
}



ENAMEfieldCssClassesとしてu-boldが設定されていることを前提としています。getElementsByClassNameを呼び出して列ENAMEを要素として取り出すために使います。ですので、効果の無いCSSクラスでも問題ありません。列ENAMEが要素として取り出せるのであれば、他の方法(タグから取得するなど)でもかまいません。



アクションの入れ替えによる実装


グリッド・ビューで行を選択した後に単一行ビューに切り替えると(グリッド・ビューで選択したときにすでにイベントが発生しているため)、選択の変更[対話グリッド]のイベントが発生しません。動的アクションが実行されないと列ENAMEに色が付かず、黒で表示されます。その状況を補完するため、単一行ビューへの切り替えを行のアクション・メニューから呼び出したときに、列ENAMEに色をつける処理も同時に実行します。

ページ・プロパティJavaScriptページ・ロード時に実行に、以下のコードを記述します。

let view = apex.region("emp").widget().interactiveGrid("getViews", "grid"),
menu$ = view.rowActionMenu$;

// 行メニューの設定をかえる 。
menu$.menu("option").items[0] =
{
type:"action",
label:"Alt Single Row View",
action: function(menu, element) {
// 最初に単一行ビューに切り替える。
apex.region( "emp" ).widget().interactiveGrid( "getActions" ).invoke( "single-row-view" );
// 単一ビューに切り替えた後は、すべてPureなJavaScriptによるDOMの操作を行なう。
let elem = document.getElementById("emp");
let enameElems = elem.getElementsByClassName("u-bold");
let deptnoElems = elem.getElementsByClassName("this-deptno");
let deptnoValElems = deptnoElems[0].getElementsByClassName("a-RV-fieldValue");
let deptno = deptnoValElems[0].innerText;
if (enameElems.length == 1) {
switch(deptno)
{
case '10':
if (enameElems[0].classList.contains("u-success-text")) {
enameElems[0].classList.remove("u-success-text");
}
if (enameElems[0].classList.contains("u-warning-text")) {
enameElems[0].classList.remove("u-warning-text");
}
if (! enameElems[0].classList.contains("u-danger-text")) {
enameElems[0].classList.add("u-danger-text");
}
break;
case '20':
if (enameElems[0].classList.contains("u-success-text")) {
enameElems[0].classList.remove("u-success-text");
}
if (enameElems[0].classList.contains("u-danger-text")) {
enameElems[0].classList.remove("u-danger-text");
}
if (! enameElems[0].classList.contains("u-warning-text")) {
enameElems[0].classList.add("u-warning-text");
}
break;
case '30':
if (enameElems[0].classList.contains("u-warning-text")) {
enameElems[0].classList.remove("u-warning-text");
}
if (enameElems[0].classList.contains("u-danger-text")) {
enameElems[0].classList.remove("u-danger-text");
}
if (! enameElems[0].classList.contains("u-success-text")) {
enameElems[0].classList.add("u-success-text");
}
break;
}
}
}
};


部門番号の値を取り出すために、列DEPTNOfieldCssClassesとしてthis-deptnoを設定しています。


以上で実装は完了です。アプリケーションを実行すると最初のGIF動画のように動きます。

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

実装を推奨しているわけではありませんが、Oracle APEXのアプリケーション作成の参考になれば幸いです。