2024年6月25日火曜日

対話グリッドのリフレッシュ前後で行の選択を維持する

対話グリッドは単体で編集可能なのでフォームを付けるケースはあまりありませんが(一般的には対話モード・レポートにフォームを付ける)、フォームをつけたときにフォームを閉じると行の選択がリセットされます。この動作自体は、対話グリッドの通常のリフレッシュ処理なのですが、行の選択を維持したいという相談がありました。

実装が思ったよりも大変だったので、記事にしました。

サンプル・データセットのEMP/DEPTに含まれるEMP表をデータ・ソースとした対話グリッドと、フォームを作成しました。以下のように動作します。


対話グリッドを含むページのページ・プロパティに、対話グリッドで選択した行(の配列)を保持するための変数selectedIdsを宣言します。

JavaScriptファンクションおよびグローバル変数の宣言に以下を記述します。

var selectedIds;

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


イベントのダイアログのクローズを受けて、対話グリッドをリフレッシュするTRUEアクションは作成済みです。

リフレッシュの直前に、以下のJavaScriptのコードを実行するTRUEアクションを作成します。対話グリッドで選択されている行のID(主キーの値)の配列をselectedIdsに保存します。
const model = apex.region("emp").call("getViews", "grid").model;
selectedIds = model.getSelectedRecords().map(
    (rec) => {
        return model.getRecordId(rec);
    }
);
// console.log(selectedIds);

対話グリッドのリフレッシュが完了した後に、selectedIdsに保存した行を選択した状態に戻します。

対話グリッドに動的アクションを作成します。タイミングイベントページ変更[対話グリッド]を選択します。ほとんどのリフレッシュ可能なコンポーネントは、イベントとしてリフレッシュ後を選べるのですが、対話グリッドではリフレッシュ後のイベントが発生しません。従って、それに近いイベントとしてページ変更を使います。

また、実行タイプとしてデバウンスを選択し、ページ変更のイベントが発生してから100ミリ秒待機したのちに処理を開始するように設定します。100ミリ秒というのは、人が気が付かないほどの短時間で、かつ、JavaScriptの処理が空振りしない程度の時間として選んでいます。


TRUEアクションとして実行するJavaScriptコードは以下になります。行の選択はfetchRecordsの処理が完了した後に実行するようにしています。
// console.log('after', selectedIds);
const model = apex.region("emp").call("getViews", "grid").model;
model.fetchRecords(
    apex.region("emp").call("setSelectedRecords", selectedIds)
);

以上で実装は完了です。

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

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