2022年10月26日水曜日

ページ・アイテムに含まれるコロンの扱いについて

 ページ・アイテムの値に含まれるコロンの扱いについて、調べてみました。

ふたつのページ・アイテムP1_DATE1P1_DATE2があり、送信ボタンでページを送信します(時刻以外のデータを後で投入するため、タイプはテキスト・フィールドとします)。


ブランチを作成して別ページに遷移します。ページ・アイテムの値を引き継ぐように、ブランチターゲットを設定します。


結果を確認します。ページ・アイテムP1_DATE2の秒の値が引き継がれていません。

内部動作を確認してみます。以下のPL/SQLスクリプトをSQLコマンドにて実行します。
declare
    l_url varchar2(4000);
    l_prepared_url varchar2(4000);
begin
    l_url   := 'f?p=102:2:0::::P1_DATE1,P1_DATE2:2022-10-12 01:15:03,2022-10-25 00:30:02';
    l_prepared_url := apex_util.prepare_url(
        p_url => l_url
        , p_plain_url=>false
    );
    dbms_output.put_line(l_prepared_url);
 end;

APEX_UTIL.PREPARE_URLのP_URLに与える値は以下です。

f?p=102:2:0::::P1_DATE1,P1_DATE2:2022-10-12 01:15:03,2022-10-25 00:30:02

以下の結果が得られます。

f?p=102:2:0::::P1_DATE1,P1_DATE2:2022-10-12%2001%3A15%3A03,2022-10-25%2000%3A30:02&cs=12kqt5FOaWV4L6visvq5RIzLHr2w2OjPGYYc9RzAIZW9YqzdELIY1VUVzbo3KaTVesLRcZdC0_cSWVxikLnsVdw

チェックサムを除くと以下になります。

f?p=102:2:0::::P1_DATE1,P1_DATE2:2022-10-12%2001%3A15%3A03,2022-10-25%2000%3A30:02

マニュアルでは、ページ・アイテムにカンマが含まれる可能性がある場合は、バックスラッシュ(\)でページ・アイテムの値を囲むように案内されています。


itemValues

URLでセッション・ステートを設定するために使用されるアイテム値のリストです。アイテム値にカンマを渡すには、文字をバックスラッシュで囲んでください。次に例を示します。

\123,45\

backslash comma (\,)以外のすべての文字列は、バックスラッシュで囲むことができます。


しかし、コロンについてはそのような記述はありません。また、カンマとは異なり、つねに区切り文字として扱われているわけでもありません。ページ・アイテムP1_DATE1については、秒の値も引き継がれています。

調べたところ、最後のコロン以降はPrinterFriendlyとして認識することが理由でした。

PrinterFriendly

ページを「印刷用」モードでレンダリングするかどうかを決定します。PrinterFriendlyが「はい」に設定されている場合は、ページが「印刷用」モードでレンダリングされます。PrinterFriendlyの値は、リージョンなどの要素をページから削除して印刷される出力を最適化するための条件のレンダリングに使用されます。

印刷用プリファレンスを参照するには、次の構文を使用します。

V('PRINTER_FRIENDLY')

参照すると、印刷用テンプレートを使用してページが表示されます。APEXエンジンは、HTMLフォーム・フィールド内のすべてのテキストをテキストとして表示します。印刷用テンプレートには、#FORM_OPEN#タグまたは#FORM_CLOSE#タグは必要ありません。目的は、表をほとんど使用しないで、印刷に適した書式で情報を表示できるようにすることです。


ターゲットのページでPrinterFriendlyの値を確認するために、ページ・アイテムを追加します。ソースタイプとして、PL/SQL式に以下を記述します。

V('PRINTER_FRIENDLY')


ボタンをひとつ追加し、こちらのボタンで実行されるブランチのターゲットは以下のように、ページ・アイテムをバックスラッシュで囲みます。


バックスラッシュ付きのブランチを呼び出してみます。


今度はP1_DATE2の秒の値も正しく渡されています。

P1_DATE2の秒の部分がPrinterFriendlyとして扱われていることを確認するため、秒の部分YESを記載して送信してみます。

P1_DATE2の秒の値は00になりますが、Printer FriendlyがYESになっていることが確認できます。

先ほどのPL/SQLの確認スクリプトのP_URLを、以下にして実行してみます。

f?p=102:2:0::::P1_DATE1,P1_DATE2:\2022-10-12 01:15:03\,\2022-10-25 00:30:02\


チェックサムの部分を除くと、以下のURLが返されます。

f?p=102:2:0::::P1_DATE1,P1_DATE2:%5C2022-10-12%2001%3A15%3A03%5C,%5C2022-10-25%2000%3A30%3A02%5C

元々は以下なので、:02の部分を含んだ形で値がエスケープされていることがわかります。

f?p=102:2:0::::P1_DATE1,P1_DATE2:2022-10-12%2001%3A15%3A03,2022-10-25%2000%3A30:02

もともとOracle APEXのURLがf?p構文のときは、コロンがセパレータとして使用されていました。簡易URLが導入されてからコロンはセパレータとして現れませんが、今回の事象は簡易URLがONでも発生します

簡単なアプリケーションですが、今回の検証に使用したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/page-item-separator.sql

以上になります。









2022年10月25日火曜日

選択リストの値をリージョンのタイトルに設定する

 ひとつのページに選択リストがあり、その選択リストで選択した値をレポート・リージョンのタイトルとします。動的アクションを作成し、選択を変更すると同時にタイトルも更新します。リージョンのタイトルを宣言的に更新する機能をOracle APEXは提供していないため、JavaScriptを記述してHTMLを直接更新します。

以下のような動作になります。

以下、実装手順になります。サンプル・データセットのEMP/DEPTに含まれる表EMPをレポートに、表DEPTを選択リストに使用します。

アプリケーション作成ウィザードを起動します。ページの追加をクリックし、追加ページよりクラシック・レポートのページを選択します。


ページ名EMPとします。表またはビュークラシック・レポートを選択します。表またはビューとしてEMPを選択します。

ルックアップ列としてDEPTNOルックアップ・キー)とDEPT.DNAME表示列)のペア、MGREMP.ENAMEのペアを指定します。DEPTNOの表示列として指定するDEPT.DNAMEは自動的に生成されるLOVで(参照制約より生成される)、これはアプリケーション作成後に追加する選択リストのページ・アイテムにも使用します。

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


 アプリケーションの作成を実行します。


アプリケーションが作成された後、ページ・デザイナにてクラシック・レポートのページを開きます。

最初に部門の選択リストを作成します。

ページ・アイテムを作成します。識別名前P1_DEPTNOタイプとして選択リストラベルDeptとします。LOVタイプとして共有コンポーネントLOVとしてDEPT.DNAMEを選択します。追加値の表示OFFにします。


ページ・アイテムP1_DEPTNOで選択した部門でクラシック・レポートの表示が絞り込まれるよう、ソースWHERE句に以下を記述します。また、動的アクションにてレポートの表示を変えるため、送信するページ・アイテムP1_DEPTNOを含めます。

:P1_DEPTNO is null or :P1_DEPTNO = DEPTNO


クラシック・レポートの識別のタイトルがEmployeesになっています。ページ・アイテムP1_DEPTNOの表示値をレポート・リージョンのタイトルにするために、このタイトルを以下に変更します。

<span id="emp_title">Employees</span>


 リージョンのテンプレートにStandardが選択されている場合、リージョンの静的IDにempを割り当てているとリージョンのタイトルはIDのemp_headingとしてアクセスできます。ただし、APEXによって生成されているリージョンの内部要素に直接アクセスすることは推奨されていません。そのため、内部要素の変更があっても変わらずにタイトルにアクセスできるよう、タイトルにspanタグとidを含めます。このようにすることで、APEXのバージョンアップによる影響を最小限にできます。

選択リストP1_DEPTNOの値が変更されたときに、リージョンのタイトルを変更する動的アクションを作成します。

動的アクションのタイミングは、イベント変更選択タイプアイテムアイテムとしてP1_DEPTNOを選択します。


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

document.getElementById("emp_title").textContent
    = apex.items.P1_DEPTNO.displayValueFor(this.triggeringElement.value);

選択リストの表示値をリージョンのタイトルに設定しています。クライアント側の条件にて、P1_DEPTNOnullではない、つまり選択リストで値が選択されているときに限定します。

初期化時に実行ONにします。


選択リストに値が選択されていない場合は、リージョンのタイトルをEmployeesとします。

TRUEアクションを作成し、以下のコードを実行します。

document.getElementById("emp_title").textContent
    = "Employees";

クライアント型の条件タイプとして、先ほどのTRUEアクションの反対になるアイテムはnullを選択します。


最後にレポートのリージョンリフレッシュを行うTRUEアクションを作成します。


デフォルトではレポート・リージョンのタイトルは非表示になっています。

外観テンプレート・オプションを開きます。


HeaderVisible - Defaultに変更します。


以上でアプリケーションは完成です。実行すると先頭のGIF動画のように動作します。

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

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

2022年10月7日金曜日

Oracle APEXが提供しているJavaScriptライブラリを含んだデバッグ

あまり発生して欲しくはないですが、自分で書いたコードに問題があるのか、Oracle APEXが提供しているJavaScriptライブラリに問題があるのか、切り分けたいことがあります。

ChromeでJavaScriptコンソールを開いて、発生している例外のスタックトレースを確認できます。

そのままの状態では、ミニファイされたソースの位置が表示されます。


開発者ツール・バーからデバッグを有効にすると(情報で十分)、ブラウザにミニファイされていないJavaScriptのファイルが読み込まれます。


同じ操作を行い例外を発生させると、今度はミニファイされていないソースの位置が表示されます。


ソースの位置をクリックすると、その位置のJavaScriptのソースが表示されます。コードを読むこともできますし、必要に応じてブレーク・ポイントも設定できます。


デバッグしたい処理を再実行すると、ブレーク・ポイントで処理が停止します。


変数の内容を確認したりステップ実行を行なって、問題の切り分けを進めます。

簡単ですが以上です。