2023年9月4日月曜日

Oracle JETのoj-bind-for-each要素をOracle APEXで扱う

Oracle JET Cookbookに記載されているoj-bind-for-eachのサンプルをOracle APEXで実装してみます。

作成されたアプリケーションは以下のように動作します。


実装の元にしたoj-bind-for-eachのサンプルは、Oracle JET Cookbookの以下のページです。


Oracle APEXのアプリケーションでは、Oracle JETでのデータ操作の結果をOracle APEXに取り込む実装を追加しています。

JETのform-containerからユーザーを取り出すボタンLIST_USERSと、取り出したユーザーを表示するページ・アイテムP1_USERSを作成しています。

以下より実装手順を説明します。

アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。名前JET Bind For Eachとします。今回の実装ではデータベースのデータは参照せず、HTMLとJavaScriptだけの実装です。そのためOracle JET Cookbookのサンプルを、あまり変更せずに利用できます。


アプリケーションが作成されたら、ページ・デザイナホーム・ページを開きます。

最初にBreadcrumb BarにあるリージョンJET Bind For Each削除します。


Body以下にリージョンを新規作成します。

識別タイトルFor Eachタイプとして静的コンテンツを選択します。ソースHTMLコードとして以下を記述します。これはOracle JET Cookbookのdemo.htmlのform-container要素を抜き出したもので、変更はありません。

<div id="form-container">
<h4>List of Users</h4>
<oj-button id="addPerson" class="oj-button-sm" on-oj-action="[[addUser]]">Add User</oj-button>
<ul>
<oj-bind-for-each data="[[dataProvider]]">
<template>
<li class="oj-flex oj-sm-align-items-center">
<oj-bind-text value="[[$current.observableIndex]]"></oj-bind-text>
:
<oj-bind-text value="[[$current.data.name]]"></oj-bind-text>
<oj-button
class="oj-button-sm oj-sm-margin-1x-vertical"
chroming="borderless"
display="icons"
on-oj-action="[[removeUser]]">
<span slot="startIcon" class="oj-ux-ico-close"></span>
Remove
</oj-button>
</li>
</template>
<template slot="noData">
<div class="oj-typography-body-md oj-typography-bold oj-sm-padding-2x-top">
No Users to display
</div>
</template>
</oj-bind-for-each>
</ul>
</div>

外観テンプレートとしてBlank with Attributes (No Grid)を選択します。


ページ・プロパティJavaScriptファイルURLとして以下を記述します。

[require jet]

ファンクションおよびグローバル変数の宣言として以下を記述します。

var simple;

ページ・ロード時に実行として以下を記述します。

Oracle JETのボタンoj-buttonon-oj-actionが指定されている場合、このボタンをクリックするとページの送信が実行されます。これはAPEXで定義されている処理が行われるためです。Oracle JETではイベントに対してpreventDefaultを呼び出すことができないため、Oracle JETが生成するボタン要素に属性type="button"を設定することにより、APEX側でページの送信を実行しないようにしています。

require(["require", "exports", "ojs/ojbootstrap", "knockout", "ojs/ojarraydataprovider", "ojs/ojknockout", "ojs/ojbutton"], function (require, exports, ojbootstrap_1, ko, ArrayDataProvider) {
"use strict";
class SimpleModel {
constructor() {
this.userIdCount = 0;
this.users = ko.observableArray([]);
/*
this.users = ko.observableArray([
{
name: "Bert",
},
{
name: "Charles",
},
{
name: "Denise",
},
]);
*/
this.dataProvider = new ArrayDataProvider(this.users, {
keyAttributes: "name",
});
this.removeUser = (event, current, bindingContext) => {
this.users.remove(current.data);
};
this.addUser = (event) => {
this.users.push({
name: "User " + this.userIdCount++,
});
};
}
}
(0, ojbootstrap_1.whenDocumentReady)().then(() => {
/*
* APEXによるページ送信を抑制するため、JETが生成するbuttun要素にtype="button"を追加する。
* 以下のForumを参照。
* https://forums.oracle.com/ords/apexds/post/how-to-avoid-a-oj-button-submit-the-page-8005
*/
let form = document.getElementById("form-container");
let ojbutton = form.querySelector("oj-button#addPerson");
$(ojbutton).ready(() => {
ojbutton.querySelector("button").setAttribute("type", "button");
});
/*
* form-containerの初期化。
*/
simple = new SimpleModel();
ko.applyBindings(simple, form);
/*
* 初期化後にデータを追加する。
*/
simple.users.push({ name: "Bert" });
simple.users.push({ name: "Charles" });
simple.users.push({ name: "Denise" });
});
});
view raw demo-foreach.js hosted with ❤ by GitHub

CSSファイルURLとして以下を記述します。

#JET_CSS_DIRECTORY#redwood/oj-redwood-notag-min.css
https://static.oracle.com/cdn/fnd/gallery/2310.0.1/images/iconfont/ojuxIconFont.min.css

ユーザー名の右隣に表示されている X をクリックすると、そのユーザーが削除されます。この X はCSSクラスoj-ux-ico-closeを指定することにより表示されています。

このアイコンを表示するため、ojuxIconFont.min.cssを読み込んでいます。このファイルに定義されているフォントの一覧は、以下より参照できます。2023年9月現在でのバージョンで、随時更新されるようです。


UPTAKE INSTRUCTIONSをクリックしてもエラーになります。Oracle APEXについては、上記のojuxIconFont.min.cssCSSファイルURLに指定することで、これらのフォントを利用できるようになります。

この状態でアプリケーションを実行すると、Oracle JET Cookbookの状態まで実装できていることが確認できます。

画面に表示されているユーザーの一覧は、Oracle JETのコンポーネントで操作されます。操作の結果(ユーザーの削除や追加)をOracle APEXで取得するために、ボタンとページ・アイテムを追加します。

ボタンLIST_USERSを作成します。ラベルList Users動作アクションとして動的アクションで定義を選択します。


画面に表示されているユーザー名を設定するページ・アイテムP1_USERSを作成します。タイプテキスト・フィールドラベルUsersとします。


ボタンLIST_USERSに動的アクションを作成します。

識別名前onClick LIST_USERSタイミングイベントはデフォルトのクリックです。


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

画面上に表示されているユーザーを取得し、そのユーザー名を:区切りで連結したのち、ページ・アイテムP1_USERSに設定します。

let users = '';
simple.users().forEach ((element, index) => {
if (index > 0) {
users = users + ":";
}
users = users + element.name;
});
apex.items.P1_USERS.setValue(users);


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

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

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