Oracle JET CookbookにあるForEach Binding (Nested)をOracle APEXで実装してみます。
カテゴリが複数あり、それぞれのカテゴリに複数の製品が含まれています。カテゴリ単位の繰り返しは、ArraryTreeDataProviderをデータ・プロバイダとしたoj-bind-for-each要素で実装します。カテゴリに含まれている製品は、ArrayTreeDataProviderのメソッドgetChildDataProviderを呼び出して取得したデータ・プロバイダを使って表示します。
作成されたアプリケーションは以下のように動作します。
以下のクイックSQLのモデルより、表AG_CATEGORIESとAG_PRODUCTSを作成します。Oracle JET Cookbookでは、produce.jsonとしてサンプル・データが提供されていますが、少量なので手入力することにして、データ・ロードの実装は省きます。
# prefix: ag
categories
name vc80 /nn
products
name vc80 /nn
SQLの生成、SQLスクリプトを保存、レビューおよび実行を順次実施します。表の作成までを行い、アプリケーションは作成しません。
アプリケーション作成ウィザードを起動します。アプリケーションの名前はJET Bind For Each (Nested)とします。
ホーム・ページの編集をクリックし削除を実行したのち、ページの追加を行います
ページ名はFruits and Vegetables、形式は積上げを選択します。表(マスター表)としてAG_CATEGORIES、ディテール表としてAG_PRODUCTSを選択します。
以上でページの追加をクリックします。
その他の設定はせず、アプリケーションの作成を実行します。
アプリケーションが作成されます。今回はすべての機能をページFruits and Vegetablesに実装します。
ページ・デザイナでページFruits and Vegetablesを開きます。
Breadcrumb BarにあるJET Bind For Each (Nested)を削除します。JET Bind For Each (Nested)の上でコンテキスト・メニューを表示させ、削除を実行します。
積み上げ形式のマスター・ディテール編集画面は、マスター表AG_CATEGORIESとディテール表AG_PRODUCTSをデータ・ソースとした2つの対話グリッドから作られます。これらの対話グリッドのデータの整合性を保つため、対話グリッドにある保存のボタンを非表示にし、代わりに一度のクリックで表AG_CATEGORIESとAG_PRODUCTSの両方の保存を行なうボタンSAVE(保存)が作成されます。
このボタンSAVEの動作のアクションはページの送信です。ページの送信ではHTTPのPOSTリクエストがサーバーに発行され、ページの再ロードが行われます。Oracle JETで実装したリージョンはページを再ロードせずに更新されるようにしたいので、マスター・ディテールでの保存を少々変更します。
対話グリッドAg Productsを選択し、プロパティ・エディタの属性タブを開きます。
ツールバーのコントロールの保存ボタンにチェックを入れます。
プロセス・ビューを開きプロセスAG_PRODUCTS - 対話グリッド・データの保存を選択します。
サーバー側の条件のボタン押下時をSAVEから- 選択 -(つまり無指定)に戻します。
以上で表AG_PRODUCTSについては、ページの送信をせずにデータ操作ができるようになりました。
アプリケーションを実行し、サンプルのデータを入力します。必ずしもproduce.jsonと同じデータでなくても、アプリケーションの動作は確認できます。
識別のタイトルはFor Each (Nested)、タイプとして静的コンテンツを選択します。ソースのHTMLコードに以下を記述します。Oracle JET Cookbookのdemo.htmlとほぼ同じ内容です。
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
<div id="form-container"> | |
<h4>Fruits and Vegetables</h4> | |
<ul> | |
<oj-bind-for-each data="[[categories]]"> | |
<template data-oj-as="category"> | |
<li> | |
<oj-bind-text value="[[category.data.name]]"></oj-bind-text> | |
<ul> | |
<oj-bind-for-each data="[[categories.getChildDataProvider(category.data.name)]]"> | |
<template data-oj-as="item"> | |
<li> | |
<oj-bind-text | |
value="[[item.data.name]]"> | |
</oj-bind-text> | |
</li> | |
</template> | |
</oj-bind-for-each> | |
</ul> | |
</li> | |
</template> | |
</oj-bind-for-each> | |
</ul> | |
</div> |
余計な装飾を除くため、外観のテンプレートとしてBlank with Attributes (No Grid)を選択します。
リージョンの描画に使うデータは、Ajaxコールバックを呼び出して取得します。
プロセス・ビューを開き、Ajaxコールバックにプロセスを作成します。
識別の名前はGET_DATA、タイプとしてコードを実行を選択します。ソースのPL/SQLコードとして以下を記述します。
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
declare | |
l_response clob; | |
begin | |
select | |
json_arrayagg( | |
json_object( | |
key 'name' value name | |
,key 'products' value products | |
) | |
) into l_response | |
from | |
( | |
select | |
c.name name | |
,json_arrayagg( | |
json_object( | |
key 'name' value p.name | |
) | |
) as products | |
from ag_categories c join ag_products p on c.id = p.category_id | |
group by c.name | |
); | |
htp.p(l_response); | |
end; |
以下のようなJSONのデータが返されます。
Oracle JET Cookbookのサンプルには属性childrenが含まれています。メソッドgetChildDataProviderが扱う属性のデフォルトがchildrenであるため、特別な指定が不要になります。今回はArrayTreeDataProviderのインスタンス作成時にchildrenAttributeとして属性productsを明示しているため、productsが含まれています。
[
{
"name": "Fruit",
"products": [
{
"name": "Orange"
},
{
"name": "Apple"
},
{
"name": "Banana"
}
]
},
{
"name": "Vegetable",
"products": [
{
"name": "Celery"
},
{
"name": "Spinach"
},
{
"name": "Corn"
}
]
}
]
レンダリング・ツリーに戻ります。
ページ・プロパティのJavaScriptのファイルURLに以下を記述します。
[require jet]
ファンクションおよびグローバル変数の宣言に以下を記述します。
var simple;
ページ・ロード時に実行に以下を記述します。
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
require(["require", "exports", "ojs/ojbootstrap", "knockout", "ojs/ojarraytreedataprovider", "ojs/ojknockout", "ojs/ojbutton"], function (require, exports, ojbootstrap_1, ko, ArrayTreeDataProvider) { | |
"use strict"; | |
class SimpleModel { | |
update(data) { | |
this.data(data); | |
}; | |
constructor() { | |
this.data = ko.observableArray(); | |
this.categories = new ArrayTreeDataProvider(this.data, { | |
keyAttributes: "name", | |
childrenAttribute: "products", | |
}); | |
} | |
} | |
(0, ojbootstrap_1.whenDocumentReady)().then(() => { | |
simple = new SimpleModel(); | |
ko.applyBindings(simple, document.getElementById("form-container")); | |
apex.actions.invoke("update-nested"); | |
}); | |
}); | |
/* | |
* 表示を更新する。 | |
*/ | |
apex.actions.add([ | |
{ | |
name: "update-nested", | |
action: () => { | |
apex.server.process ( "GET_DATA", {}, | |
{ | |
success: (data) => { | |
// console.log(data); | |
simple.update(data); | |
} | |
} | |
); | |
} | |
} | |
]); |
対話グリッドAg Productsでデータを変更し保存したときに、JETの表示を更新します。
対話グリッドAg Productsに動的アクションを作成します。
識別の名前はonSAVE Productsとします。タイミングのイベントとして保存[対話グリッド]を選択します。
TRUEアクションとしてJavaScriptコードの実行を選択し、設定のコードに以下の1行を記述します。
apex.actions.invoke("update-nested");
以上でアプリケーションは完成です。アプリケーションを実行すると、記事の先頭のGIF動画のように動作します。
今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/jet-bind-for-each-nested.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完