ラベル IG の投稿を表示しています。 すべての投稿を表示
ラベル IG の投稿を表示しています。 すべての投稿を表示

2022年9月1日木曜日

対話グリッドの特定のセルを読み取り専用にする

Twitterを見ていたらカナダのInsum Solutionsより、Elisabeth Ashleyさんが"How to Set Specific Cells of Oracle APEX Interactive Grids to Read Only"という記事を書きましたよ、とツイートがありました。そのツイートに対話グリッドを開発しているJohn Snydersより、check callbackを使った方が簡単では?と返信が付いていました。

John Snydersが紹介しているCheckCalbackを使って、Insum Solutionsの記事にある実装を書き換えてみました。

記事とは条件を変えています。対話グリッド上で、上司のいない従業員の給与と手当を変更不可にします。

サンプル・データセットEMP/DEPTに含まれる表EMPをソースとして、対話グリッドを作成します。その対話グリッドに静的IDとしてempを設定します。

対話グリッドの属性詳細JavaScript初期化コードとして、以下を記述します。


設定は以上です。

動作を確認してみます。


Managerが設定されている行は、Salary、Commissionともに編集できます。Managerが設定されていないKINGのHiredは変更できますが、SalaryとCommissionは変更できません。ただし、一時的にManagerに値を設定するとSalaryとCommissionに値を入力でき、データベースに保存することもできます。これは、読取専用にしているのはあくまで対話グリッド上のセルであって、サーバー側には制限がかかっていないためです。

新規業の場合は、Managerに値がなくてもSalaryとCommissionを設定できます。

おおむね想定どおりの実装ができています。

対話グリッド上で一時的にManagerを変更しSalaryやCommissionを変更するという抜け道を禁止するには、対話グリッドでManagerの変更を禁止するという方法もありますが、サーバーに検証を作成する方が確実です

検証を作成し、名前を給与の変更禁止とします。検証編集可能リージョンとして対話グリッドのリージョンを選択します。タイプとしてファンクション本体(ブールを返す)を選択し、PL/SQLファンクション本体として以下を記述します。

エラーエラー・メッセージは「上司のいない従業員の給与は変更できません。」とします。関連付けられた列としてSALを選択します。


同様に検証として手当の変更禁止を作成します。

エラーエラー・メッセージ関連付けられた列は、給与、SALから手当COMMに変更します。


以上でManagerが設定されていない従業員の給与と手当を更新しようとすると、サーバー側でエラーが発生するようになりました。


以上でアプリケーションは完成です。

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

Oracle APEX JavaScript APIリファレンスのmodelインターフェスが、主に関連する部分です。
https://docs.oracle.com/en/database/oracle/apex/22.1/aexjs/model.html#.CheckCallback
https://docs.oracle.com/en/database/oracle/apex/22.1/aexjs/model.html#check
https://docs.oracle.com/en/database/oracle/apex/22.1/aexjs/model.html#.RecordFieldMetadata

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

2022年8月26日金曜日

対話グリッドの行の追加ボタンを非表示にする

 対話グリッドの操作は主にツール・バー上のボタン、アクション・メニュー行操作メニューより行います。

本記事では、対話グリッドのツール・バー上のボタン行の追加行操作メニュー行の追加表示/非表示を切り替える方法を紹介します。

どの操作も1行のJavaScriptのコードを実行します。

最初にOracle APEX JavaScript API ReferenceのWidgetsのinteractiveGridのページを開きます。

https://docs.oracle.com/en/database/oracle/apex/22.1/aexjs/interactiveGrid.html

ActionsのセクションにあるPre-defined actions used by the Interactive Grid widgetより、表示/非表示を切り替える対象であるアクションの内部名称を見つけます。

ツール・バー上の行の追加はselection-add-rowです。行操作メニューの行の追加はrow-add-rowです。

WebブラウサのJavaScriptコンソールを開き、これらの表示/非表示をコンソールから操作します。

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

最初にツール・バー上の行の追加ボタンを非表示します。

apex.region("emp").call("getActions").hide("selection-add-row");

コマンドの実行によりツール・バー上の行の追加ボタンが消えます。


表示させるにはhideの代わりにshowを呼び出します。

apex.region("emp").call("getActions").show("selection-add-row");

再度、ツール・バー上に行の追加ボタンが表示されます。


行操作メニュー行の追加を非表示にします。

apex.region("emp").call("getActions").hide("row-add-row");

行操作メニューより行の追加が無くなっています。


表示を戻します。

apex.region("emp").call("getActions").show("row-add-row");

行操作メニュー行の追加が含まれています。


行の追加以外のアクションも同様に操作することができます。

以上です。

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

2022年8月19日金曜日

JavaScriptのコードを書いて対話グリッドを操作する

 ボタンをクリックして対話グリッドに新規行を追加したい、という相談がありました。対話グリッドを操作するためのAPIは概ね公開されているため、JavaScriptでコードを書けば大抵の操作は可能です。かなり凝ったUIなどを実装できますが、アプリケーションの保守性は下がります。

確認のために実装してみました。対話グリッドのソースとして、サンプル・データセットEMP/DEPTに含まれる表EMPを使用しています。

ページ・アイテムP1_ENAMEP1_SALを作成し、それぞれ新規行の列ENAMESALの初期値として扱えるようにします。また、対話グリッドの静的IDとしてempを設定しています。

JavaScript API ReferenceのWidgets - interactiveGridのPre-defined actions used by the Interactive Grid widgetの表より、呼び出すアクションを見つけます。新規行の挿入はアクションrow-add-rowまたはselection-add-rowによって実行できます。それぞれの動作は、以下のように説明されています。

row-add-row = Insert a row straight after the current row.

selection-add-row = Insert a row straight after any selected rows. If no rows are selected, or if cell selection mode is enabled, the new row will be added at the beginning.

アクションrow-add-rowを実行するには、ボタンのクリックで実行される動的アクションとして、以下のJavaScriptコードを記述します。

apex.region("emp").call("getActions").invoke("row-add-row");


新規行を挿入するだけであれば、これで完了です。

Oracle APEX 22.1より、ボタンなどから直接アクション(actions.action)を呼び出すことができるようになりました。この新しい設定では動的アクションの代わりに、ボタンの詳細カスタム属性data-action=としてアクションを直接指定します。

data-action="[emp]row-add-row"


row-add-rowの代わりにアクションselection-add-rowを呼び出すと、新規行は対話グリッドの先頭ではなく、選択されている行の下に挿入されます。

次にページ・アイテムP1_ENAME、P1_SALの値を初期値として、新規行に設定します。

対話グリッドに動的アクションを作成します。

識別名前New Recordとします。タイミングイベント行の初期化[対話グリッド]を選択します。選択タイプリージョンリージョンEmployeesです。


TRUEアクションとして、ページ・アイテムの値を列に設定するコードを記述します。ただし、タイミングイベント行の初期化[対話グリッド]なので、作成したボタンのクリック以外、例えば、対話グリッド上で行の追加をクリックしたとき、および、すでにある行をクリックして編集モードにしたときも、列の値を初期値で置き換えてしまいます

それを避けるためにisDefaultRequiredというフラグを変数として定義します。

TRUEアクションとしてJavaScriptコードの実行を選択します。設定コードとして以下を記述します。
isDefaultRequired = false;
let model = this.data.model;
let r = this.data.record;
model.setValue(r, "ENAME", apex.items.P1_ENAME.value);
model.setValue(r, "SAL", apex.items.P1_SAL.value);
クライアント側の条件タイプとしてJavaScript式を選択し、JavaScript式isDefaultRequiredと記述します。isDefaultRequiredがtrueのときのみ、初期値の設定を行います。


グローバル変数isDefaultRequiredを定義します。ページ・プロパティJavaScriptファンクションおよびグローバル変数の宣言に、以下を記述します。

var isDefaultRequired = false;


ボタンを押したときに初期値を設定するため、TRUEアクションのコードにフラグの変更を含めます。
isDefaultRequired = true;
apex.region("emp").call("getActions").invoke("row-add-row");

以上で新規行を作成したときに、列に初期値が設定されるようになりました。

この他に、対話グリッドのmodelインターフェースのinsertNewRecordを呼び出す方法がOracle Communityにポストされています。


回答として記述されているコードで使用しているinsertNewRecordのメソッドは、新規行のレコードIDを返却します。そのため、そのレコードに調節初期値を設定できます。初期値を設定するために、対話グリッドに動的アクションを作成する必要はありません。

ボタンを押した時に新規行の作成と初期値の設定を同時に行うコードは、以下になります。
// 対話グリッドの静的IDにempが設定されている。
let gridView = apex.region("emp").widget().interactiveGrid("getViews").grid;
// カレント・ビューはgrid以外にchartなどもあり得る。
// let gridView = apex.region("emp").call("getCurrentView");
let model = gridView.model;
// 最終行を見つける。
// let row$ = apex.region("emp").widget().find(".a-GV-row").last();
// 先頭行を見つける。
// let row$ = apex.region("emp").widget().find(".a-GV-row").first();
// 行の選択を変更する。
// gridView.view$.grid("setSelection", row$);
// 選択されている行を取り出す。
let sel = gridView.getSelectedRecords();
// 選択されている行の下に一行追加する。
let rId = model.insertNewRecord(null, sel[sel.length - 1]);
// レコードIDからレコード・オブジェクトを取得する。
let r = model.getRecord(rId);
// 追加された行に列に値を設定する。
model.setValue(r, 'ENAME', apex.items.P1_ENAME.value);
model.setValue(r, 'SAL', apex.items.P1_SAL.value);
新規行を作成する位置もコード内で指定します。上記のコードでは、現在選択されている行の下に新規行を作成します。


ちなみに対話グリッドに限りませんが、Oracle APEXのフロントエンドのコンポーネントのソースコードにアクセスすることもできます。対話グリッドのmodelインターフェースのコードは、以下より参照できます。

https://static.oracle.com/cdn/apex/22.1.0/libraries/apex/model.js

ソース・コードはOracle APEXのダウンロード・メディアにも含まれています。

先ほど対話グリッドの動的アクションとして、新規行の初期値を設定しました。このためにフラグisDefaultRequiredを作成しています。Oracle APEX 22.1で新設されたdata-action=の指定で、初期値を設定するコーディングを行なってみます。

ページ・プロパティJavaScriptページ・ロード時に実行に、以下のコードを記述します。
apex.actions.add([
{
    name: "my-insert-new-record",
    action: function( event, element, args) {
        let gridView = apex.region("emp").widget().interactiveGrid("getViews").grid;
        let model = gridView.model;
        let sel = gridView.getSelectedRecords();
        let rId = model.insertNewRecord(null, sel[sel.length - 1]);
        let r = model.getRecord(rId);
        model.setValue(r, 'ENAME', apex.items.P1_ENAME.value);
        model.setValue(r, 'SAL', apex.items.P1_SAL.value);
    }
}
]);
アクションとしてmy-insert-new-recordを作成しています。このアクションでは新規行の作成以外に、初期値の設定も行なっています。


このアクションを呼び出すには、ボタン詳細カスタム属性として、以下を記述します。

data-action="#action$my-insert-new-record"


以上で、ボタンをクリックしたときにアクションmy-insert-new-recordが実行され、対話グリッドに新規行が作成されます。

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

対話グリッドの操作を行うボタンは8つあります。

B_ROW_ADD_ROW = アクションrow-add-rowを動的アクションから呼び出す。
B_DATA_ACTION_ROW_ADD_ROW = アクションrow-add-rowをdata-actionより呼び出す。
B_SELECTION_ADD_ROW = アクションselection-add-rowを動的アクションから呼び出す。
B_DATA_ACTION_SELECTION_ADD_ROW = アクションselection-add-rowをdata-actionより呼び出す。
B_INSERT_NEW_RECORD = insertNewRecordを動的アクションより呼び出す。
B_SELECTION_ADD_ROW_LAST = アクションselect-add-rowを呼び出し、対話グリッドの一番下に新規行を作成する。
B_ACTION_LINK = アクションmy-insert-new-recordを呼び出す。
B_REFRESH = アクションrefreshを呼び出す。

最近のアプリケーションでは、SPA = Single Page Applicationのようにページ遷移を行わない、または、極力減らすような実装が増えていると思います。APEXの場合、動的アクションを多用することになります。この場合、いろいろな場所にJavaScriptのコードが分散して書かれるため、アプリケーションの保守性が下がります。actions.actionのアクションの活用により、このような状況が改善されることが期待されています。JavaScriptのコードはページ・プロパティに記述されるか静的ファイルに記述されることになり、ページ中のコンポーネントに埋め込むことを避けることができます。

今回の記事は以上になります。

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

2022年6月7日火曜日

対話グリッドで選択した行を引数としてプロセスを実行する

 ダイアログとして開かれた対話グリッド上で選択した列を引数にして、プロセスを実行したいという相談がありました。

元々は対話グリッドは別のページに作成し、それをモーダル・ダイアログとして開こうとしていたのですが、(できなくはないが)それだと実装が難しくなります。

代わりにインライン・ダイアログで実装してみました。Always FreeのAutonomous Databaseで動くAPEX 21.2上で、アプリケーションを作成しています。

作成したアプリケーションは、以下のような動作をします。

最初は対話グリッド上で選択した行に含まれる従業員名を、直接ページ・アイテムに設定します。その後、対話グリッド上では従業員番号を取り出し、サーバー側に送信して従業員名に変換した上でページ・アイテムに設定します。

アプリケーションで作成するデータは、サンプル・データセットEMP/DEPTに含まれる表EMPを使用します。

アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。名前プロセス呼び出しとします。アプリケーションはホーム・ページに実装します。


ページ・デザイナホーム・ページを開きます。

Body静的コンテンツのリージョン従業員を作成します。


対話グリッド上で選択した、従業員の名前を保持するページ・アイテムの作成を行います。

識別名前P1_ENAMEタイプテキスト・フィールドラベル従業員名とします。


従業員を選択するために使用する、対話グリッドのリージョンの作成を行います。

識別タイトル従業員選択タイプ対話グリッドソース表名としてEMPを指定します。外観テンプレートInline Dialogを選択します。


行を選択するために使用するので、Attributes編集有効OFFにします。


インライン・ダイアログである従業員選択のリージョンを開く、ボタンの作成を行います。

識別ボタン名B_SELECTラベル従業員選択動作アクションとして動的アクションで定義を選択します。


ボタンに、動的アクションの作成を行います。

識別名前従業員選択をクリックとします。タイミングイベントクリック選択タイプボタンボタンB_SELECTとなります。


TRUEアクション識別リージョンを開く影響を受ける要素は、選択タイプリージョンリージョン従業員選択とします。これで、ボタンを押すと対話グリッドが開きます。


対話グリッド上で行を選択したときに実行される、動的アクションの作成を行います。

識別名前従業員の選択とします。タイミングイベント選択の変更[対話グリッド]選択タイプリージョンリージョン従業員選択とします。


最初は、従業員名を直接、ページ・アイテムP1_ENAMEに設定します。

TRUEアクション識別アクション値の設定を選択します。設定タイプの設定JavaScript ExpressionJavaScript式に以下を指定します。

this.data.selectedRecords[0][1]

選択される行は複数可であるため配列ですが、今回は常に1行です。そのため、最初の引数は常に次の引数列ENAMEの位置を表す1になります。(位置ではなく列名を指定したいときはmodelインターフェースを介す必要があります。こちらの記事では対話グリッドのmodelインターフェースを使用しています。)影響を受ける要素選択タイプアイテムアイテムP1_ENAMEになります。実行オプション初期化時に実行OFFクライアント側の条件タイプJavaScript式JavaScript式として以下を記述します。

this.data.selectedRecords.length > 0

行が選択されているときに限定して、値の設定(つまりP1_ENAMEへの値の設定)が実行されます。


対話グリッドのAttributes外観最初の行の選択OFFにします。この設定がONの場合、対話グリッドを開いた時点で最初の行が選択されます。その時点で対話グリッドのイベント選択の変更[対話グリッド]が発火し、動的アクション行の選択が実行されてしまいます。


値の設定を行った後に、対話グリッドのリージョンを閉じます。

TRUEアクションを作成し、識別アクションとしてリージョンを閉じるを選択します。影響を受ける要素選択タイプリージョンリージョン従業員選択を選びます。クライアントの条件は、値の設定と同じく、タイプJavaScript式JavaScript式は以下になります。

this.data.selectedRecords.length > 0


以上で、対話グリッド上で選択した行の従業員名が、ページ・アイテムP1_ENAMEに設定されます。

これから、今回の目的であるサーバー側のプロセスを呼び出す実装を行います。

Ajaxコールバックとして、プロセスget_enameを作成します。引数x01として従業員番号を受け取り、それより従業員名を検索します。結果をJSONフォーマットで呼び出し元に返します。

declare
   l_response varchar2(4000);
begin
   select json_object(ename) into l_response
   from emp
   where empno = apex_application.g_x01;
   htp.p(l_response);
   apex_debug.info(l_response);
exception
    when others then
        htp.p('{}');
end;

呼び出し側を変更します。

TRUEアクションの値の設定を、JavaScriptコードの実行に変更します。設定コードでは、先ほど作成したAjaxコールバックget_enameを呼び出すコードを記述します。
apex.server.process(
    "get_ename"    // Simple Echoback writen in PL/SQL is called, running inside DB server.
    , {
        x01: this.data.selectedRecords[0][0]  // The argument on the browser passed to the server process.
    }
    ,{
        success: function( data ) 
        {
            apex.items.P1_ENAME.setValue(data.ENAME);
        },
        error: function ( jqXHR, textStatus, errorThrown )
        {
            // do something here
        }
    }
);

以上で、アプリケーションは完成です。アプリケーションを実行すると、記事の最初のGIF動画の動作をします。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。

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

2022年5月26日木曜日

レポート上で選択した行のIDをページ・アイテムに設定する

 レポート上で行を選択して、その情報を使いたいというのはよくある要件です。簡単に実装方法をまとめてみました。

サンプルの実装には、サンプル・データセットに含まれるEMP/DEPTのEMPのデータを使用しています。表EMPに含まれる従業員を選択し、選択された従業員の従業員番号をページ・アイテムに設定します。

対話グリッドでは動的アクションとして実装します。行セレクタや選択行の変更イベントなどが標準で実装されているため、追加で記述するコードが少なくて済みます。また、動的アクションなので、選択を確定するためにページを送信する必要がありません。

推奨は、対話グリッドを使った実装です。

クラシック・レポートでも実装できます。APEX_ITEM.CHECKBOX2を使って、チェックボックの列をレポートに追加します。


以下より、両方の実装について紹介します。


アプリケーションの作成


あらかじめサンプル・データセットEMP/DEPTをインストールします。

SQLワークショップユーティリティサンプル・データセットからインストールします。

アプリケーション作成ウィザードを起動します。作成するアプリケーションの名前サンプル・レコード選択とします。アプリケーションには、対話グリッドとクラシック・レポートのページを作成します。

最初にホーム・ページの編集を開いて、ページを削除します。

ページの追加をクリックし、対話グリッドを選択します。

ページ名対話グリッドとし、表またはビュー編集を許可を選択します。表またはビューとして表EMPを選択します。

以上で、ページの追加をクリックします。


再度、ページの追加をクリックします。今度はクラシック・レポートを追加します。追加ページを開いて、クラシック・レポートを選択します。


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

以上で、ページの追加をクリックします。


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


以上で、アプリケーションの準備は完了です。


対話グリッドでの実装



対話グリッドの行セレクタを使って、選択した行をページ・アイテムに設定します。

対話グリッドのページを開き、静的コンテンツのリージョンを作成します。作成したリージョンにページ・アイテムP1_SELECTED_EMPLOYEESを作成します。このページ・アイテムに選択した行、つまり複数の従業員の従業員番号を','区切りで設定します。


対話グリッドのリージョンEmployees動的アクションを作成します。

識別名前従業員の選択とします。タイミングイベントとして選択の変更[対話グリッド]を指定します。選択タイプリージョンリージョンEmployeesになります。


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


対話グリッドでの実装は以上で完成です。

対話グリッドのAttributesの設定を変更し、行の追加行の更新行の削除をできないようにしたり、ツール・バー表示OFFにして、行の選択以外できないようにすると、より使いやすくなるでしょう。



クラシック・レポートでの実装


クラシック・レポートでも同様に、選択した従業員を設定するページ・アイテムP2_SELECTED_EMPLOYEESを作成します。さらに、送信ボタンB_SUBMITも作成します。ボタンの動作アクションページの送信とします。


クラシック・レポートのリージョンEmployeesを選択し、ソースタイプSQL問合せに変更します。レポートにチェックボックスが含まれるよう、SQL問合せにAPEX_ITEM.CHECKBOX2を含んだSELECT文を記述します。




追加した列SelectはHTMLが生成されるため、セキュリティ特殊文字をエスケープOFFにします。


レポート上での選択結果は、ページの送信時にAPEX_APPLICATION.G_F01としてサーバーに渡されます。それを受け取って、ページ・アイテムP2_SELECTED_EMPLOYEESに設定するプロセスを作成します。

作成したプロセスの名前従業員の選択とし、ソースPL/SQLコードとして以下を記述します。




以上でクラシック・レポートでの実装も完了です。

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

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