対話モード・レポートのデータ・ソースとしてサンプル・データセットのEMP/DEPTに含まれる表EMPを使用します。作成したアプリケーションは以下のように動作します。
ボタンOpenのクリックで対話モード・レポートを、非モーダル・ダイアログのページとして開きます。ボタンSeachのクリックで対話モード・レポートへのフィルタの適用、ボタンClearのクリックでフィルタを解除します。
以下より、アプリケーションの実装を簡単に紹介します。
ボタンOPEN、SEARCH、CLEARはホーム・ページに作成しています。ページ・プロパティのJavaScriptのファイルURLに、これらのボタンの動作となるAPEXアクションを記述したファイルを設定します。
[module]#APP_FILES#js/main#MIN#.js
ボタンOPEN、SEARCH、CLEARがクリックされたときに実行されるAPEXアクションを定義しています。それぞれのアクションは最初にAjaxコールバックとして実装されているデータベースの処理を呼び出し、その後にSEARCHおよびCLEARでは、レポートをリフレッシュするイベントをBroadcastChannelに渡しています。ボタンOPENは、データベースが生成したURLをターゲットとして、ウィンドウを開いています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 非モーダル・ダイアログに表示されているレポートの表示を制御する。 | |
*/ | |
const channel = new BroadcastChannel('exchange-event'); | |
const controlsElement = document.getElementById("CONTROLS"); | |
const controls = apex.actions.createContext("controls", controlsElement); | |
/* | |
* レポートを開いているウィンドウを保持する変数。 | |
*/ | |
var reportWindow = null; | |
/* | |
* ボタンのカスタム属性data-actionに基づいて、ボタンのアクションを実行する。 | |
*/ | |
controls.add([ | |
{ | |
name: "SEARCH", | |
action: (event, element, args) => { | |
apex.server.process( | |
"ADD_FILTER", | |
{ | |
pageItems: "#P1_ENAME" | |
}, | |
{ | |
success: (data) => { | |
/* | |
* 属性reportとして、リフレッシュ対象のレポートの静的IDが返される。 | |
* 返されたレポートの静的IDは、そのままイベントに渡す。 | |
*/ | |
channel.postMessage({ type: "refresh", target: data }); | |
} | |
} | |
); | |
} | |
}, | |
{ | |
name: "CLEAR", | |
action: (event, element, args) => { | |
apex.server.process( | |
"CLEAR_REPORT", | |
{}, | |
{ | |
success: (data) => { | |
// CLEARもSERCHと同様、Ajaxコールバックのレスポンスをそのまま渡す。 | |
channel.postMessage({ type: "refresh", target: data }); | |
// ページ・アイテムP1_ENAMEをクリアする。 | |
apex.item("P1_ENAME").setValue(null); | |
} | |
} | |
); | |
} | |
}, | |
/* | |
* OPENについては、APEXアクションを使って定義する必要はありません。 | |
* | |
* ボタンのプロパティの動作のアクションに、このページにリダイレクトを選択し、 | |
* 宛先のターゲットとなるページを設定すれば、以下と同様の処理が行われます。 | |
* | |
* APEX_PAGE.GET_URLのターゲットが標準ページとダイアログで、返されるURLが | |
* 異なることを示すために、この例を含めている。 | |
*/ | |
{ | |
name: "OPEN", | |
action: (event, element, args) => { | |
apex.server.process( | |
"GET_URL", | |
{}, | |
{ | |
success: (data) => { | |
const url = data.url; | |
apex.debug.info("url: ", url); | |
if ( url.startsWith("javascript:") ) { | |
/* | |
* URLがjavascript:で始まる場合、ダイアログとしてページを開く。 | |
* URLはJavaScriptとして実行される。 | |
*/ | |
apex.navigation.redirect(url); | |
} | |
else | |
{ | |
/* | |
* URLがjavascript:で始まらない標準ページは、新しいウィンドウで開く。 | |
* ウィンドウのハンドラを取得するために、noopener: falseを指定する。 | |
*/ | |
reportWindow = apex.navigation.openInNewWindow(url, "_blank", { noopener: false }); | |
apex.debug.info("reportWindow: ", reportWindow); | |
} | |
} | |
} | |
); | |
} | |
} | |
]); |
ボタン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"を設定しています。すべてのボタンの動作のアクションは動的アクションで定義に設定します。
アプリケーション・アイテム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'
対話モード・レポートはページ番号2に作成しています。
外観のページ・モードとして非モーダル・ダイアログを選択し、JavaScriptのファイルURLとして以下を設定しています。
[module]#APP_FILES#js/report#MIN#.js
対話モード・レポートのソースの表名としてEMPを設定します。また、フィルタの設定に使用するリージョンIDを取得するため、およびリフレッシュ対象のリージョンを特定するために、静的IDにEMPを設定します。
ページに読み込むreport.jsのコードは以下です。BroadcastChannelよりリフレッシュの実行を指示するイベントを受け付けて、対話モード・レポートのリフレッシュを実行します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* メインのページから送信されたイベントに基づいて、レポートを制御する。 | |
*/ | |
const channel = new BroadcastChannel('exchange-event'); | |
apex.debug.info("channel: ", channel); | |
channel.addEventListener("message", (event) => { | |
apex.debug.info("event: ", event); | |
if ( event.data.type === "refresh" ) { | |
apex.region(event.data.target.report).refresh(); | |
} | |
}); |
今回作成したAPEXアプリケーションの紹介は以上になります。
Oracle APEXのページで外観のページ・モードがダイアログ、つまりモーダル・ダイアログまたは非モーダル・ダイアログである場合、JavaScriptリファレンスに記載のないファンクションapex.theme42.dialogを呼び出してページを開いています。そのため、ダイアログとして開かれたウィンドウのハンドルは、簡単には取得できません。ページ・モードがタイアログではなく標準ページであれば、ページをオープンする時にウィンドウ・ハンドルを取得できます。
ウィンドウを特定してイベントのやり取りを行う場合は、ページ・モードをダイアログにしない方が良いでしょう。
今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-exchange-event.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完