2023年5月9日火曜日

knockout.jsのチュートリアルをOracle APEXで実装してみる

 ドイツ在住のMaik Michelさんが彼のブログにOracle APEXでknockout.jsを使用する方法を紹介しています。

Improve UX by using Knockout - show only functions that actually make sense

興味深い内容だったので、knockout.jsのサイトで紹介されているチュートリアルのIntroductionをOracle APEXで実装してみました。

チュートリアルのページは以下から始まります。


Step1から5までありますが、最後のStep 5はまとめで実際の実装サンプルは1から4までです。


準備


Oracle APEXでknockout.jsを使用するために、ページ・プロパティJavaScriptファイルURLに以下を記述します。バージョンについては、将来のAPEXのバージョンで変更される可能性があります。

#JET_BASE_DIRECTORY#js/libs/knockout/knockout-3.5.1.js


Oracle JETのサイトで紹介されているように、Oracle JETはMVVM Architectureを採用しており、knockoutを含んでいます。Oracle APEXは主にチャートの表示にOracle JETを採用しているため、Oracle APEXにもknockoutは含まれます。



ステップ1:Welcome!の実装


最初の例ではHTML要素のテキスト・ノードの更新を行っています。

knockout.jsのチュートリアルでは、以下のHTMLを記述しています。

<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>

Oracle APEXでは、これらの要素をタイプ表示のみページ・アイテムとして作成することにします。

First nameの要素は、識別名前P1_FIRST_NAMEタイプ表示のみラベルFirst name:とします。ラベルと値が横並びになるように、外観テンプレートOptionalを選択します。

詳細カスタム属性に、knockout.jsが使用するカスタム属性data-bindを指定します。

data-bind="text: firstName"

前のテキスト<strong>後のテキスト</strong>を記述します。これはknockout.jsのチュートリアルに寄せた設定で、Oracle APEXでは、ユニバーサル・テーマで定義されている強調表示のクラスu-bold詳細CSSクラスに設定する方が一般的です。

デフォルトタイプ静的を選択し、静的値としてtodoを設定します。これもknockout.jsのチュートリアルに寄せた設定です。


Last nameの要素も同様に作成します。

識別名前P1_LAST_NAMEラベルLast name:です。詳細カスタム属性は以下になります。

data-bind="text: lastName"


ページ・プロパティJavaScriptファンクションおよびグローバル変数の宣言にて、ビューモデルを定義します。
function AppViewModel() {
    this.firstName = "Bert";
    this.lastName = "Bertington";
}


DOM要素にビューモデルをバインドするコードを、ページ・ロード時に実行に記述します。
ko.applyBindings(new AppViewModel());


以上の設定を行いページを実行します。

ページのロード時にページ・アイテムP1_FIRST_NAMEP1_LAST_NAMEの値が、それぞれビュー・モデルで定義されているBertBeringtonに変更されています。



Step 2:Making the data editableの実装



先に作成したWelcome!のページをコピーし、Step 2の実装を始めます。

チュートリアルでは、以下のインプット・フィールドを追加しています。

<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>

これらの要素をタイプテキスト・フィールドページ・アイテムとして作成することにします。

First nameの要素は、識別名前P2_FIRST_NAME_VALUEタイプテキスト・フィールドとします。

詳細カスタム属性data-bindでは、textの代わりにvalueを指定します。

data-bind="value: firstName"

詳細前のテキスト後のテキスト<strong>は削除します。それ以外の設定は表示のみのページ・アイテムP2_FIRST_NAMEと同じです。


Last nameの要素も同様に作成します。

属性名前P2_LAST_NAME_VALUE詳細カスタム属性は以下になります。

data-bind="value: lastName"


ページ・プロパティJavaScriptファンクションおよびグローバル変数の宣言にて定義されているビューモデルを変更します。

firstNameおよびlastNameko.observableを指定し、自動的に変更を通知するようにしています。
function AppViewModel() {
    this.firstName = ko.observable("Bert");
    this.lastName = ko.observable("Bertington");
}

以上の設定を行いページを実行すると、ページ・アイテムP2_FIRST_NAME_VALUEP2_LAST_NAME_VALUEが変更されると即座にP2_FIRST_NAMEP2_LAST_NAMEに反映されることが確認できます。


今回の例ではビューモデルの定義をknockout.jsのチュートリアルに寄せましたが、Oracle APEXの一般的な実装では、ko.observableの引数として与える初期値はページ・アイテムの値となるでしょう。

コード例としては以下になります。
function AppViewModel() {
    this.firstName = ko.observable(apex.items.P2_FIRST_NAME_VALUE.value);
    this.lastName = ko.observable(apex.items.P2_LAST_NAME_VALUE.value);
}


Step 3:Defining computed valuesの実装



Step 2のページをコピーし、Step 3の実装を始めます。

チュートリアルでは、以下の氏名を表示するフィールドを追加しています。

<p>Full name: <strong data-bind="text: fullName"></strong></p>

この要素をタイプ表示のみページ・アイテムとして作成することにします。

Full nameの要素は、識別名前P3_FULL_NAMEタイプ表示のみラベルFull name:とします。

詳細カスタム属性に以下を設定します。

data-bind="text: fullName"


firstNameとlastNameを結合しfullNameを生成するように、ビューモデルを更新します。
function AppViewModel() {
    this.firstName = ko.observable("Bert");
    this.lastName = ko.observable("Bertington");

    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();    
    }, this);
}

以上でページを実行すると、First nameおよびLast nameを変更した時点で、Full nameが更新されることが確認できます。



Step 4:Add more behaviorの実装



Step 3のページをコピーし、Step 4の実装を始めます。

チュートリアルでは、Last nameを大文字に変更するボタンを作成します。

<button data-bind="click: capitalizeLastName">Go caps</button>

Oracle APEXでは上記をボタンとして作成します。

識別ボタン名GO_CAPSラベルGo Capsとします。動作アクションとして動的アクションで定義を選択します。動的アクションは定義しないので、実質的にはHTTPのGETやPOSTのリクエスト発行しない、何もしないボタンになります。

詳細カスタム属性として以下を記述することにより、ボタンをクリックするとビューモデルで定義された処理が実行されます。

data-bind="click: capitalizeLastName"


ビューモデルを更新します。Last nameを大文字に変更する処理を含めます。
function AppViewModel() {
    this.firstName = ko.observable("Bert");
    this.lastName = ko.observable("Bertington");

    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();    
    }, this);

    this.capitalizeLastName = function() {
        var currentVal = this.lastName();        // Read the current value
        this.lastName(currentVal.toUpperCase()); // Write back a modified value
    };    
}

以上の設定を行いページを実行します。

Go Capsのボタンをクリックすると、Last nameが大文字に変更されることが確認できます。また、同時にFull nameの変更も確認できます。


Oracle APEXでのknockout.jsのチュートリアルの実装は以上になります。

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

本チュートリアルではknockout.jsのtext、value、clickの3つのバインディングを使用していますが、knockout.jsのドキュメントではこれ以外にも多数のバインディングが説明されています。

Oracle APEXでは機能として動的アクションが提供されていますが、knockout.jsの利用も良い選択肢となりそうです。

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