2025年1月15日水曜日

非モーダル・ダイアログにあるレポートをダイアログを開いたウィンドウから制御する

非モーダル・ダイアログのページに対話モード・レポートを作成し、そのページを開いたページよりレポートのフィルタの適用と解除をします。非モーダル・ダイアログを開いたページと、そのページから開かれたダイアログのページはWebブラウザのウィンドウとしては異なっているため、ウィンドウ間のイベントのやり取りにBroadcastChannelを使用します。

対話モード・レポートのデータ・ソースとしてサンプル・データセットEMP/DEPTに含まれる表EMPを使用します。作成したアプリケーションは以下のように動作します。

ボタンOpenのクリックで対話モード・レポートを、非モーダル・ダイアログのページとして開きます。ボタンSeachのクリックで対話モード・レポートへのフィルタの適用、ボタンClearのクリックでフィルタを解除します。


以下より、アプリケーションの実装を簡単に紹介します。

ボタンOPENSEARCHCLEARはホーム・ページに作成しています。ページ・プロパティJavaScriptファイルURLに、これらのボタンの動作となるAPEXアクションを記述したファイルを設定します。

[module]#APP_FILES#js/main#MIN#.js


main.jsの内容です。

ボタンOPENSEARCHCLEARがクリックされたときに実行されるAPEXアクションを定義しています。それぞれのアクションは最初にAjaxコールバックとして実装されているデータベースの処理を呼び出し、その後にSEARCHおよびCLEARでは、レポートをリフレッシュするイベントをBroadcastChannelに渡しています。ボタンOPENは、データベースが生成したURLをターゲットとして、ウィンドウを開いています。


ボタンSEARCHをクリックしたときに実行されるAjaxコールバックADD_FILTERには、以下のPL/SQLコードを記述して、apex_ir.add_filterを呼び出します。
declare
    C_REPORT_COLUMN constant varchar2(5) := 'ENAME';
    C_PAGE_ID       constant number := 2;
    C_REGION_ID     constant number := :G_REPORT_REGION_ID;
begin
    apex_ir.add_filter(
        p_page_id        => C_PAGE_ID
        ,p_region_id     => C_REGION_ID
        ,p_report_column => C_REPORT_COLUMN
        ,p_filter_value  => :P1_ENAME
        ,p_operator_abbr => 'EQ'
        ,p_report_id     => null
    );
    /*
     * フィルタを適用したレポートの静的IDを属性reportとして返す。
     */
    htp.p('{ "success": true, "report": "EMP" }');
end;

ボタンCLEARをクリックしたときに実行されるAjaxコールバックCLEAR_REPORTには、以下のPL/SQLコードを記述して、apex_ir.clear_reportを呼び出します。
declare
    C_REPORT_COLUMN constant varchar2(5) := 'ENAME';
    C_PAGE_ID       constant number := 2;
    C_REGION_ID     constant number := :G_REPORT_REGION_ID;
begin
    apex_ir.clear_report(
        p_page_id        => C_PAGE_ID
        ,p_region_id     => C_REGION_ID
        ,p_report_id     => null
    );
    /*
     * リセットしたレポートの静的IDを属性reportとして返す。
     */
    htp.p('{ "success": true, "report": "EMP" }');
end;

ボタンOPENをクリックしたときに実行されるAjaxコールバックGET_URLには、以下のPL/SQLコードを記述して、apex_page.get_urlを呼び出します。
declare
    l_url varchar2(400);
    l_response clob;
begin
    l_url := apex_page.get_url(
        p_page => 2
    );
    l_response := apex_string.format('{ "url": "%s" }', l_url );
    htp.p(l_response);
end;

ボタンを含む静的コンテンツのリージョンに、APEXアクションのコンテキストを作成しています。リージョンを特定するために静的IDとしてCONTROLSを設定しています。


APEXアクションを呼び出すために、ボタンのカスタム属性としてdata-action=を設定しています。ボタンOPENにはdata-action="OPEN"SEARCHにはdata-action="SEARCH"CLEARにはdata-action="CLEAR"を設定しています。すべてのボタンの動作アクション動的アクションで定義に設定します。


フィルタを適用する対話モード・レポートのレポートIDを、セッションの開始時にアプリケーション・アイテムG_REPORT_REGION_IDに保存します。レポートIDはアプリケーションの実行時に変わることはないため、あらかじめ取得しておくことで、フィルタを設定するたびにレポートIDを取得しないようにします。


アプリケーション・アイテムG_REPORT_REGION_IDアプリケーションの計算を作成します。計算タイプSQL問合せ(単一の値を返す)頻度計算ポイント新規インスタンス(新規セッション)開始時を選択します。
select region_id from apex_application_page_regions
where application_id = :APP_ID and page_id = 2 and static_id = 'EMP'

対話モード・レポートはページ番号に作成しています。

外観ページ・モードとして非モーダル・ダイアログを選択し、JavaScriptファイルURLとして以下を設定しています。

[module]#APP_FILES#js/report#MIN#.js


対話モード・レポートのソース表名としてEMPを設定します。また、フィルタの設定に使用するリージョンIDを取得するため、およびリフレッシュ対象のリージョンを特定するために、静的IDEMPを設定します。


ページに読み込むreport.jsのコードは以下です。BroadcastChannelよりリフレッシュの実行を指示するイベントを受け付けて、対話モード・レポートのリフレッシュを実行します。


今回作成したAPEXアプリケーションの紹介は以上になります。

Oracle APEXのページで外観ページ・モードがダイアログ、つまりモーダル・ダイアログまたは非モーダル・ダイアログである場合、JavaScriptリファレンスに記載のないファンクションapex.theme42.dialogを呼び出してページを開いています。そのため、ダイアログとして開かれたウィンドウのハンドルは、簡単には取得できません。ページ・モードがタイアログではなく標準ページであれば、ページをオープンする時にウィンドウ・ハンドルを取得できます。

ウィンドウを特定してイベントのやり取りを行う場合は、ページ・モードをダイアログにしない方が良いでしょう。

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

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