2023年9月7日木曜日

Knockoutを無効にした状態でOracle JETのコンポーネントを使用する

Oracle APEXはOracle JETのコンポーネントをチャートの実装に活用していますが、knockout.jsは使用していません。以下のドキュメントに説明があります。

Oracle APEXアプリケーション・ビルダー・ユーザーズ・ガイド Release 22.2
10.1 Oracle APEXとのOracle JETの統合の理解
10.1.2 Oracle JETがOracle APEXと統合する仕組み

ノート:Oracle JETには双方向データ・バインディング(knockout.jsを使用)が用意されていますが、現在のAPEXでは、ツールキットのこの側面はネイティブに使用されません。
Oracle JETのカスタム・エレメントが静的コンテンツとして生成される場合、または動的コンテンツであっても、カスタム・エレメントをIDで特定できる場合は、そのカスタム・エレメントに対応するバインディング・コンテキスト(JavaScriptのオブジェクト)を作成してknockout.jsのapplyBindingsを呼び出すことができます。しかし、レポート(クラシック・レポート、対話モード・レポート、対話グリッド)の列にJETチャートを表示する場合や、カード・リージョンのカード上に表示するケースでは、難しい実装になります。

そのようなケースでもOracle JETのチャートを利用できるように、属性itemsが用意されています。属性itemsにチャートとして表示するデータ(JSONドキュメント)を与えると、そのデータを使ったチャートが描画されます。

ただし、Oracle JETはknockout.jsと一緒に使用することを想定しているためか、Oracle JET API ReferencesのそれぞれのElementsAttributes一覧にitemsは含まれていません。Type DefinitionsItemがあれば、それをひとつのオブジェクトとした配列をitemsに与えることができるようです。

本記事では以下のようなアプリケーションを作成し、Knockoutを使用しないJETチャートの実装方法を確認します。


JET Picto Chartをサンプルの実装に使用します。Picto Chart(カスタム・エレメントはoj-picto-chart)のリファレンスより、以下のType DefinitionsItemを参照します。


Picto Chartとして表示されるそれぞれのアイテムには、表示色とツールチップの情報が必要です。Itemの定義を参照するとcolornameがその情報に対応することがわかります。

colorstring<optional>
The color of the item. Does not apply if custom image is specified.

namestring<optional>
The name of the item. Used for default tooltip and accessibility.

以上より属性itemsには以下のようなデータを与えることにします。
[
    {
        "color": 色情報,
        "name": "ツールチップとなる文字列"
    },
    {
        "color": 色情報,
        "name": "ツールチップとなる文字列"
    },
   ... 繰り返し
]
アプリケーションで使用するデータは、Open-Meteo.comのFree Weather APIを呼び出して取得しています。使用にあたっては、ライセンスおよびPricingについて、ご自身で確認していただくようお願いします。Terms & Privacyに説明がありますが、非商用であれば無料で利用できます。

以下より実装について説明します。

Open-Meteo.comのAPIを呼び出す際に引数とする都市のマスター・データを保存する表HMT_CITIESと、その都市ごとの最高気温を保存する表HMT_CITY_TEMPERATURESを作成します。

表の作成には、以下のクイックSQLのモデルを使用します。
# prefix: hmt
cities
	city_name vc80 /nn
	latitude num /nn
	longitude num /nn
	timezone vc20 /nn
	city_temperatures
		date_rec date /nn
		temperature_2m_max num
SQLの生成SQLスクリプトを保存レビューおよび実行を順次実施します。表の作成までを行い、アプリケーションの作成は行いません。


表HMT_CITIESにいくつかの都市の情報を保存します。

SQLコマンドを開き、以下を実行します。SQLコマンドで実行できるのは1行のSQLなので、begin/endでINSERT文を囲んでいます。
begin
Insert into HMT_CITIES (CITY_NAME,LATITUDE,LONGITUDE,TIMEZONE) values ('Tokyo',35.6895,139.6917,'Asia/Tokyo');
Insert into HMT_CITIES (CITY_NAME,LATITUDE,LONGITUDE,TIMEZONE) values ('New York',40.7143,-74.006,'America/New_York');
Insert into HMT_CITIES (CITY_NAME,LATITUDE,LONGITUDE,TIMEZONE) values ('London',51.5085,-0.1257,'Europe/London');
Insert into HMT_CITIES (CITY_NAME,LATITUDE,LONGITUDE,TIMEZONE) values ('Berlin',52.5244,13.4105,'Europe/Berlin');
Insert into HMT_CITIES (CITY_NAME,LATITUDE,LONGITUDE,TIMEZONE) values ('Paris',48.8534,2.3488,'Europe/Paris');
Insert into HMT_CITIES (CITY_NAME,LATITUDE,LONGITUDE,TIMEZONE) values ('Sydney',-33.8678,151.2073,'Australia/Sydney');
end;
実行結果に1行が挿入されました。と表示されますが、エラーが発生しなければ全行挿入されています。


アプリケーション作成ウィザードを起動します。アプリケーションの名前JET without Knockoutとします。

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

表HMT_CITIESおよびHMT_CITY_TEMPERATURESの内容を手動で編集することは想定していませんが、内容を確認できるようにマスター・ディテールのページを作成します。


マスター・ディテールを選択します。


ページ名Cities and Temperatures、形式に積上げを選択します。(マスター)HMT_CITIESディテール表HMT_CITY_TEMPERATURESを指定します。

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


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


アプリケーションが作成されます。


Open-Meteoよりデータを取り込む処理とPicto Chartに与えるJSONドキュメントを表HMT_CITY_TEMPERATURESより取り出す処理を、PL/SQLパッケージHMT_UTILにまとめました。

以下のコードを実行し、パッケージHMT_UTILを作成します。SQLスクリプトなどを使用すると良いでしょう。

ページCities and TemperaturesにOpen-Meteo.comよりデータを取得する処理を組み込みます。

ページ・デザイナにてページCities and Temperaturesを開きます。

データを取得する年を指定するページ・アイテムP2_YEARを作成します。

識別タイプ選択リストラベルYear、検証必須の値オンとします。

LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。現在の年から過去30年を表示の対象として選択できます。

追加値の表示NULL値の表示オフとします。


データを取得する月を指定するページ・アイテムP2_MONTHを作成します。

識別タイプ選択リストラベルMonth、検証必須の値オンとします。ページ・アイテムP2_YEARの右隣に配置するため、レイアウト新規行の開始オフにします。

LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

追加値の表示NULL値の表示オフとします。


登録されているすべての都市を対象として、指定した1ヶ月間の日毎の最高気温のデータを、Open-MeteoのWeather APIより取得します。

ボタンLOADを作成します。

ラベルはLoad from Open-Meteo.comとします。外観テンプレート・オプションWidthとしてStretchを選択します。

動作アクションはデフォルトのページの送信のまま変更しません。


プロセス・ビューを開き、ボタンLOADをクリックしたときに実行されるプロセスを作成します。

識別名前Load from Open-Meteo.comタイプコードの実行を選択します。

ソースPL/SQLコードとして以下を記述します。

サーバー側の条件ボタン押下時LOADを指定します。


以上でデータのロードができるようになりました。

一旦ページを実行し、2023年8月のデータをロードします。

Year2023Month8を選択し、ボタンLoad from Open-Meteo.comをクリックします。

下の対話グリッドに(上の対話グリッドで)選択した都市の最高気温が表示されると、データのロードは成功しています。


ホーム・ページに都市を選択してJET Picto Chartを表示する機能を実装します。

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

Breadcrumb BarにあるリージョンJET without Knockoutと、Bodyにあるリージョンページ・ナビゲーションを削除します。


Picto Chartを表示する都市を選択するページ・アイテムP1_CITYを作成します。

タイプとして選択リストを選び、ラベルCityにします。

LOVタイプとしてSQL問合せを選択し、SQL問合せとして以下を記述します。
select city_name d, id r from hmt_cities order by city_name asc
追加値の表示オフNULL値の表示オンにしてNULL表示値- 都市を選択 -と記述します。


Picto Chartを表示するを選択するページ・アイテムP1_YEARを作成します。

タイプとして選択リストを選び、ラベルYearにします。ページ・アイテムP1_CITYの右隣に配置するため、レイアウト新規行の開始オフにします。

LOVタイプとしてSQL問合せを選択し、SQL問合せとして以下を記述します。
select to_char(date_rec,'YYYY') d, to_char(date_rec,'YYYY') r
from hmt_city_temperatures where city_id = :P1_CITY
group by to_char(date_rec,'YYYY') order by to_char(date_rec,'YYYY') desc
追加値の表示オフNULL値の表示オンにしてNULL表示値- 年を選択 -と記述します。

カスケードLOV親アイテムP1_CITYを指定し、親が必要オンにします。


Picto Chartを表示するを選択するページ・アイテムP1_MONTHを作成します。

タイプとして選択リストを選び、ラベルMonthにします。ページ・アイテムP1_YEARの右隣に配置するため、レイアウト新規行の開始オフにします。

LOVタイプとしてSQL問合せを選択し、SQL問合せとして以下を記述します。
select to_char(date_rec,'MM') d, to_char(date_rec,'MM') r
from hmt_city_temperatures 
where city_id = :P1_CITY and to_char(date_rec,'YYYY') = :P1_YEAR
group by to_char(date_rec,'MM') order by to_char(date_rec,'MM') asc
追加値の表示オフNULL値の表示オンにしてNULL表示値- 月を選択 -と記述します。

カスケードLOV親アイテムP1_CITYとP1_YEARを指定し、親が必要オンにします。


Picto Chartを表示するリージョンを作成します。

識別タイトルPicto Chartタイプ静的コンテンツです。

ソースHTMLコードに以下を記述します。属性itemsは動的アクションで設定するため、空の配列を与えています。



プロセス・ビューを開き、属性itemsに設定するデータを返すAjaxコールバックを作成します。

識別名前GET_DATAタイプとしてコードを実行を選択します。

ソースPL/SQLコードとして以下を記述します。


ページ・アイテムP1_MONTHに値が選択されたときに、動的アクションを実行してPicto Chartの描画を行います。

ページ・アイテムP1_MONTHに動的アクションを作成します。

識別名前Picto Chartの更新とします。タイミングイベントはページ・アイテムのデフォルトである変更です。

クライアント側の条件タイプとしてアイテムはnullではないを選択し、アイテムP1_MONTHを選択します。実際はP1_CITY、P1_YEARもnullだとデータの取得に失敗しますが、親が必要オンにしたカスケードLOVであるためP1_MONTHに値があれば、他のページ・アイテムにも必ず値があります。


TRUEアクションとしてJavaScriptコードの実行を選択します。

設定コードとして以下を記述します。AjaxコールバックGET_DATAを呼び出して取得したJSON形式のデータ(ノードの色、ツールチップ、形状を含む配列)をPicto Chartのitemsに設定しています。

ページのロード時にもPicto Chartの表示を試みるように、実行初期化時に実行オンにします。この場合、動的アクションに設定したクライアント側の条件は適用されません。そのため、TRUEアクションのクライアント側の条件に、動的アクションと同じ条件を設定します。


ページ・プロパティJavaScriptCSSのセクションに、Oracle JETを組み込むために必要な設定を行います。

JavaScriptファイルURLに以下を記述します。

[require jet]

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

require(["ojs/ojcore", "ojs/ojpictochart"], function(oj) {});

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

#JET_CSS_DIRECTORY#redwood/oj-redwood-notag-min.css

CSSインラインに以下を記述します。ツールチップの表示に含まれる不要な文字列を隠すために使用します。

.oj-dvt-datatip-value {
    display: none;
}


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

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

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