2023年8月29日火曜日

Oracle JETのTimelineをOracle APEXで扱う

 今までに、Oracle JETのTreemapとTag CloudをOracle APEXで扱う方法を紹介しました。これも手順としてはほぼ同じですが、今回はTimelineをOracle APEXで扱ってみます。

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


Oracle JET Cookbookで紹介されているTimelineのBasicTimelineを実装しています。


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

Oracle JET CookbookではJSON形式のファイルseriesOneData.jsonをデータソースとしています。Oracle APEXのアプリケーションでは、データベースの表をデータソースとして使用します。

seriesOneData.jsonにはタイムラインに表示するイベントのタイトル(title)、開始日(begin)、説明(description)、シリーズ(series)が記載されています。このJSONデータは後ほど、データ・ロード定義を作成する際にサンプルとして使うため、手元のPCにファイルとしてダウンロードしておきます。

以下のクイックSQLのモデルより、表TMLN_EVENTSを作成します。元のJSONデータでは属性idが定義されていますが、表TMLN_EVENTSでは列IDは主キー(サロゲート・キー)とし、属性IDは列EIDに保存します。
# prefix: tmln
events
    eid vc8 /nn /unique
    title vc80 /nn
    begin date /nn
    description vc200
    series vc80
SQLの生成SQLスクリプトを保存レビューおよび実行を順次実施します。表の作成までを実施し、アプリケーションは作成しません。


タイムラインを組み込むAPEXのアプリケーションを作成します。

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

デフォルトで作成されているホーム・ページを削除し、表TMLN_EVENTSをデータソースとした対話グリッドをページとして追加します。


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

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


アプリケーションが作成されます。タイムラインはページ番号Timelineのページに実装します。


seriesOneData.jsonを表TMLN_EVENTSにロードする際に使用する、データ・ロード定義を作成します。

共有コンポーネントデータ・ロード定義を開きます。


作成済みのデータ・ロード定義が一覧されます。作成をクリックします。


データ・ロードの作成として最初からを選択します。

へ進みます。


データ・ロード定義の名前Timelineとします。ターゲット・タイプ表名としてTMLN_EVENTSを選択します。

へ進みます。


サンプル・データのソース・タイプとしてファイルのアップロードを選択し、すでにダウンロードしているseriesOneData.jsonサンプル・ファイルとして選択します。

へ進みます。


ソース列ID(Varchar2(50))マップ先EID(Varchar2)に変更します。また、マップ先BEGIN(Date)書式マスクとしてMON DD, YYYYを設定します。それ以外は、デフォルトでソース列データベースの列名が一致しているため、ソース列に適切なマップ先が選択されています。

以上の設定でデータ・ロードの作成をクリックします。


データ・ロード定義Timelineが作成されます。設定を調整するためにTimelineを開きます。


静的IDをtimelineからTIMELINEに変更します。PL/SQLのコード中で使用するIDは大文字にしたいという理由なので、必ずしも必要な作業ではありません。設定ロード・メソッド置換に変更します。

以上の変更を行い、変更の適用をクリックします。


以上でデータ・ロード定義の作成は完了です。

ページ・デザイナでページTimelineを開きます。

Breadcrumb BarにあるJET Timelineを削除します。JET Timelineの上でコンテキスト・メニューを表示させ、削除を実行します。


表TMLN_EVENTSに初期データを投入するボタンを作成します。

識別ボタン名INITラベルInitとします。対話グリッドTmln Eventsの上に配置します。外観テンプレート・オプションWidthStretchに変更し、画面の横幅いっぱいにボタンを表示させます。

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


左ペインでプロセス・ビューを開き、ボタンINITを押した時に実行するプロセスを作成します。

識別名前初期化タイプとしてデータのロードを選択します。設定データ・ロード定義として先ほど作成したTimelineを選択し、データ・ソース型としてSQL Queryを選び、SQL問合せに以下を記述します。

select
apex_web_service.make_rest_request_b(
p_url => 'https://www.oracle.com/webfolder/technetwork/jet/cookbook/dataVisualizations/timeline/basicTimeline/seriesOneData.json'
,p_http_method => 'GET'
) as blob_content
from dual;
サーバー側の条件ボタン押下時INITを設定します。


この状態でアプリケーションを実行し、ボタンInitをクリックします。

ORA-1843: 指定した月が無効ですが発生します。これは、列BEGINのデータがJan 30, 2010といった英語の月名のデータになっているためです。このAPEXアプリケーションのアプリケーション・プライマリ言語は日本語なので、対処が必要です。


プロセス・ビューを開き、フォアグラウンドで実行する実行チェーンを作成します。

識別名前データ・ロードタイプとして実行チェーンを選択します。サーバー側の条件ボタン押下時INITを指定します。


作成済みのプロセス初期化を選択し、識別実行チェーンとしてデータ・ロードを指定します。プロセス初期化は実行チェーンデータ・ロードに含まれる1つのプロセスに変わります。

サーバー側の条件ボタン押下時- 選択 -に戻します。


実行チェーンデータ・ロードにプロセスを作成し、プロセス初期化上に配置します。

識別名前英語タイプとしてコードを実行を選択します。ソースPL/SQLコードとして以下を記述し、NLS_DATE_LANGUAGEをAMERICANに変更します。これでデータ・ロードは英語で実行されるため、ORA-1843の発生を抑制できます。

execute immediate 'alter session set nls_date_language = ''AMERICAN''';


実行チェーンデータ・ロードにもうひとつプロセスを作成します。これはプロセス初期化下に配置します。

識別名前日本語タイプとしてコードの実行を選択し、ソースPL/SQLコードに日付言語を日本語に戻すコマンドを記述します。

execute immediate 'alter session set nls_date_language = ''JAPANESE''';


アプリケーションを実行しボタンInitをクリックし、再度データ・ロードを実行します。

今度は表TMLN_EVENTSに16行のデータがロードされました。


タイムラインを表示するリージョンを作成します。

識別タイトルTimelineとします。タイプとして静的コンテンツを選択します。ボタンINITと対話グリッドの間にリージョンを配置します。

ソースHTMLコードとして以下を記述します。Oracle JET Cookbookのdemo.htmlからTimeline OrientationとTimeline Overviewの設定を切り替えるスイッチを除いています。これらはAPEXのページ・アイテムを使って実装します。

<div id="timelineContainer">
<oj-timeline
id="tline"
:aria-label="[['Overview Timeline Demo. Current date is ' + currentDateString]]"
minor-axis.scale="weeks"
minor-axis.zoom-order='["months", "weeks", "days"]'
major-axis.scale="quarters"
start='[[new Date("Jan 1, 2010").toISOString()]]'
end='[[new Date("Dec 31, 2010").toISOString()]]'
selection-mode="single"
orientation="[[orientationValue]]"
overview.rendered="[[ko.toJS(overviewValue)]]"
reference-objects="[[referenceObjects]]"
selection='["e4"]'
data="[[dataProvider]]"
class="demo-timeline">
<template slot="seriesTemplate" data-oj-as="series">
<oj-timeline-series label="[[series.id]]" empty-text="No Data."></oj-timeline-series>
</template>
<template slot="itemTemplate" data-oj-as="item">
<oj-timeline-item
series-id="[[item.data.series]]"
start="[[item.data.begin]]"
label="[[item.data.title]]"
description="[[item.data.description]]">
</oj-timeline-item>
</template>
</oj-timeline>
</div>
view raw timeline.html hosted with ❤ by GitHub

余計な装飾を除くため、外観テンプレートとしてBlank with Attributes (No Grid)を選択します。


TimelineのOrientationとOverviewの表示オプションを設定するページ・アイテムを配置するリージョンを作成します。

識別タイトルOptions To Control The Timeline Belowタイプ静的コンテンツです。ボタンINITの下、リージョンTimelineの上に配置します。


このリージョンに、タイムラインの方向を決めるページ・アイテムP1_ORIENTATIONを作成します。

タイプとして切替えを選択します。ラベルTimeline Orientationとします。設定デフォルトの使用オフにし、オン値horizontalオン・ラベルHorizontalオフ値verticalオフ・ラベルVerticalを設定します。


デフォルトタイプとして静的を選択し、静的値としてhorizontalを設定します。


続いてこのリージョンに、タイムラインのオーバービューを表示するかどうかを決めるページ・アイテムP1_OVERVIEWを作成します。

タイプとして切替えを選択します。ラベルTimeline Overviewとします。設定デフォルトの使用オフにし、オン値onオン・ラベルEnabledオフ値offオフ・ラベルDisabledを設定します。ページ・アイテムP1_ORIENTATIONの右隣に配置するため、レイアウト新規行の開始オフにします。


デフォルトタイプとして静的を選択し、静的値としてonを設定します。


タイムラインのデータはAjaxコールバックを呼び出して取得します。

プロセス・ビューを開き、Ajaxコールバックにプロセスを作成します。Ajaxコールバックは、表TMLN_EVENTSの内容をJSONの配列として返します。

識別名前GET_DATAタイプとしてコードを実行を選択します。ソースPL/SQLコードとして以下を記述します。

declare
l_response clob;
begin
select json_arrayagg(
json_object(
key 'id' value eid
,key 'title' value title
,key 'begin' value begin
,key 'description' value description
,key 'series' value series
)
) into l_response
from tmln_events;
htp.p(l_response);
end;


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

[require jet]

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

var timeline;

ページ・ロード時に実行に以下を記述します。タイムラインのモデルとなるクラスSeriesModelと、タイムラインを更新するapex.actionのupdate-timelineを定義しています。内容は概ねOracle JET Cookbookのdemo.jsを踏襲しています。

require(["require", "exports", "knockout", "ojs/ojbootstrap", "ojs/ojarraydataprovider", "ojs/ojknockout", "ojs/ojtimeline", "ojs/ojformlayout", "ojs/ojbutton"], function (require, exports, ko, ojbootstrap_1, ArrayDataProvider) {
"use strict";
class SeriesModel {
/*
* Timelineで表示するデータを更新する。
*/
update(data) {
this.dataProvider(new ArrayDataProvider(data, {
keyAttributes: "id",
}));
}
constructor() {
/*
* this.dataProviderはknockoutのobservableArrayに変更し、
* データはコンストラクタの外から設定する。
*/
this.dataProvider = ko.observableArray();
/*
this.dataProvider = new ArrayDataProvider(JSON.parse(data), {
keyAttributes: 'id'
});
*/
/*
* overviewValue, orientationValueはページ・アイテムで設定する。
*/
this.overviewValue = ko.observable(apex.items.P1_OVERVIEW.value);
this.orientationValue = ko.observable(apex.items.P1_ORIENTATION.value);
/* 変更なし */
this.currentDateString = 'Feb 1, 2010';
this.currentDate = new Date(this.currentDateString).toISOString();
this.referenceObjects = [
{
value: this.currentDate
}
];
}
}
(0, ojbootstrap_1.whenDocumentReady)().then(() => {
timeline = new SeriesModel();
ko.applyBindings(timeline, document.getElementById('timelineContainer'));
/* ページ・ロード時の表示 */
apex.actions.invoke("update-timeline");
});
});
/*
* Timelineを更新する。
*/
apex.actions.add([
{
name: "update-timeline",
action: () => {
apex.server.process ( "GET_DATA", {},
{
success: (data) => {
// console.log(data);
timeline.update(data);
}
}
);
}
}
]);
CSSファイルURLに以下を指定します。

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

インラインに以下を記述します。Oracle JET Cookbookのdemo.cssの内容です。
.demo-timeline {
    width: 100%;
    height: 32rem;
}

対話グリッドでデータを変更し保存したときに、タイムラインを更新する動的アクションを作成します。

識別名前変更の保存とします。タイミングイベントとして保存[対話グリッド]を選択し、選択タイプリージョンリージョンとしてTmln Eventsを指定します。


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

apex.actions.invoke("update-timeline");


オリエンテーションの変更をタイムラインに通知します。ページ・アイテムP1_ORIENTATIONに動的アクションを作成します。

識別名前onChange Orientationタイミングイベントはデフォルトの変更です。


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

timeline.orientationValue($v(this.triggeringElement));


オーバービューの表示の切り替えをタイムラインに通知します。ページ・アイテムP1_OVERVIEWに動的アクションを作成します。

識別名前onChange Overviewタイミングイベントはデフォルトの変更です。


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

timeline.overviewValue($v(this.triggeringElement));


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

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

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