2023年9月5日火曜日

東京の最高気温を表示するJET Picto Chartをoj-bind-for-eachとArrayTreeDataProviderを使うように変更する

以前に東京の最高気温をJET Picto Chartで表示するアプリケーションを作成しました。こちらの記事ですが、Oracle APEXの動的コンテンツとして複数月の表示を実装しています。

この実装をOracle JETのoj-bind-for-eachとArrayTreeDataProviderを使って、静的コンテンツとして実装し直します。

リージョンTemperaturesタイプ静的コンテンツに変更し、ソースHTMLコードに以下を記述します。

<div id="chart-container" class="oj-sm-padding-2x-horizontal">
<div class="oj-typography-body-lg oj-typography-bold">Daily Temperatures For &P1_TAG.</div>
<div class="oj-flex oj-sm-flex-items-initial">
<oj-bind-for-each data="[[dataProvider]]">
<template data-oj-as="calendar">
<div class="oj-flex-item oj-sm-margin-4x-end">
<div class="oj-typography-body-sm oj-typography-bold oj-sm-margin-4x-vertical">
<oj-bind-text value="[[calendar.data.month]]">
</oj-bind-text>
</div>
<div class="oj-sm-margin-1x-start demo-datavisualizations-blockcalendar-wordspacing">
S M T W T F S
</div>
<!--
ArrayTreeDataProviderのchildrenAttributeとしてtemperaturesが設定されているため、
dataProvider.getChildDataProvider(calendar.data.month)は、この月のtemperaturesとして
渡されているdateとvalueの配列がdataに渡る。
-->
<oj-picto-chart
id="pictochart1"
data="[[dataProvider.getChildDataProvider(calendar.data.month)]]"
layout="horizontal"
row-height="20"
column-count="7">
<template slot="itemTemplate" data-oj-as="item">
<oj-picto-chart-item
short-desc='[[getTooltip(calendar.data.month, item.data.date, item.data.value)]]'
color="[[getColor(item.data.value)]]">
</oj-picto-chart-item>
</template>
</oj-picto-chart>
</div>
</template>
</oj-bind-for-each>
</div>
<oj-legend
id="legend1"
class="oj-sm-padding-6x-horizontal demo-datavisualizations-blockcalendar-style"
orientation="horizontal"
data="[[legendDataProvider]]"
symbol-width="15"
symbol-height="15">
<template slot="itemTemplate" data-oj-as="item">
<oj-legend-item text="[[item.data.text]]" color="[[item.data.color]]"></oj-legend-item>
</template>
</oj-legend>
</div>


ページ・プロパティJavaScriptページ・ロード時に実行を以下に書き換えます。

require(["require", "exports", "knockout", "ojs/ojbootstrap", "ojs/ojarraydataprovider", "ojs/ojarraytreedataprovider", "ojs/ojpalette", "ojs/ojpaletteutils", "ojs/ojknockout", "ojs/ojpictochart", "ojs/ojlegend"], function (require, exports, ko, ojbootstrap_1, ArrayDataProvider, ArrayTreeDataProvider, ojpalette_1, ojpaletteutils_1) {
"use strict";
class PictoChartModel {
constructor(data) {
/* データ・プロバイダをobservableArrayで初期化 */
this.data = ko.observableArray(data);
this.dataProvider = new ArrayTreeDataProvider(this.data, {
keyAttributes: "month",
childrenAttribute: "temperatures",
});
/* ツールチップの表示形式を決める */
this.getTooltip = (month, date, value) => {
return date === 0
? ""
: `${month}-${date.toString()} (${value.toString()})°C`;
};
/* 気温による表示色を決める */
this.colors = (0, ojpalette_1.getColorValuesFromPalette)("viridis", 7);
this.getColor = (value) => {
return value === null
? "rgba(0,0,0,0)"
: (0, ojpaletteutils_1.getColorValue)(this.colors, (value + 30) / 70);
};
/* レジェンドの初期化 - 摂氏に変更 */
this.legendItems = [];
this.temp = [
"-30〜-20\xB0C",
"-20〜-10\xB0C",
"-10〜0\xB0C",
"0〜10\xB0C",
"10〜20\xB0C",
"20〜30\xB0C",
"30〜40\xB0C",
];
this.legendDataProvider = ko.observableArray();
/* レジェンドとして表示する項目の生成 */
for (let i = 0; i < this.temp.length; i++) {
this.legendItems.push({ text: this.temp[i], color: this.colors[i] });
}
this.legendDataProvider(new ArrayDataProvider(this.legendItems, {
keyAttributes: "text",
}));
}
}
(0, ojbootstrap_1.whenDocumentReady)().then(() => {
apex.server.process ( "GET_DATA", {
pageItems: ["P1_TAG","P1_YEAR","P1_MONTH"]
},
{
success: (data) => {
// console.log(data);
ko.applyBindings(new PictoChartModel(data), document.getElementById("chart-container"));
}
}
);
});
});

AjaxコールバックGET_DATAソースPL/SQLコードを以下に書き換えます。

declare
l_response_json json_array_t;
l_response clob;
l_object json_object_t;
l_days json_array_t;
l_day pls_integer;
l_date_str varchar2(7);
l_start_date date;
l_end_date date;
begin
/* 表示に使用する期間 */
l_start_date := to_date(:P1_YEAR || case when length(:P1_MONTH) = 1 then '0' else '' end || :P1_MONTH, 'YYYYMM');
l_end_date := add_months(l_start_date, :G_MONTH) - 1;
/* JET Picto Chartが扱う形式で出力 */
l_response_json := json_array_t();
for r in (
select
trunc(date_rec, 'MONTH') year_month
,json_arrayagg(
json_object(
key 'date' value extract(day from date_rec)
,key 'value' value temperature_2m_max
)
order by date_rec asc
) as value
from hmt_temperatures where tag = :P1_TAG
and date_rec between l_start_date and l_end_date
and temperature_2m_max is not null
group by trunc(date_rec, 'MONTH')
order by trunc(date_rec, 'MONTH')
)
loop
l_date_str := to_char(r.year_month, 'YYYY-MM');
l_days := json_array_t(r.value);
/* 日曜日から1日までの穴埋めをする */
l_day := to_number(to_char(r.year_month,'D'));
for i in 1..(l_day-1)
loop
l_days.put(0, json_object_t('{ "date": 0, "value": null }'));
end loop;
l_object := json_object_t();
l_object.put('month', l_date_str);
l_object.put('temperatures', l_days);
l_response_json.append(l_object);
end loop;
l_response := l_response_json.to_clob();
-- apex_debug.info(l_response);
htp.p(l_response);
end;

以上で作業は完了です。動作自体は、動的コンテンツで実装したものと同じです。

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

Oracle JETについては、英語のOracle JET Cookbookくらいしか説明が見当たらないため、APEXのアプリケーションに組み込むのは骨の折れる作業です。これまでの記事で、Oracle APEXでOracle JETを使うことが少しでも容易になれば幸いです。