Scrollable Heat Mapを組み込んだAPEXアプリケーションは以下のように動作します。
JET Cookbookに載っているサンプルとまったく同じですが、データ・ソースはファイルではなくデータベースに変更しています。
Oracle JET Cookbookに掲載されているサンプルをOracle APEXに実装する手順は、過去にいくつかの記事で紹介しています。今回のScrollable Heat Mapの組み込みも、ほぼ同様の手順です。
以下より実装手順を紹介します。
最初に表示に使用するデータをデータベースに取り込みます。以下のDDLを実行し表EBAJ_DEMO_HOUSE_PRICEを作成します。
create table ebaj_demo_house_price (
id number generated by default on null as identity
constraint ebaj_demo_house_price_id_pk primary key,
region varchar2(40 char) not null,
period varchar2(40 char) not null,
price number not null
);
以下はSQLコマンドでの実行結果です。
JET Cookbookのサンプルは、以下のファイルをデータとして読み込んでいます。
https://www.oracle.com/webfolder/technetwork/jet/cookbook/dataCollections/dataCollections/heatmapGrid/housePriceData.json
このJSONファイルを表EBAJ_DEMO_HOUSE_PRICEに読み込みます。以下のスクリプトを実行します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_response clob; | |
l_regions json_array_t; | |
l_region_object json_object_t; | |
l_keys json_key_list; | |
l_key varchar2(4000); | |
l_region ebaj_demo_house_price.region%type; | |
l_period ebaj_demo_house_price.period%type; | |
l_price ebaj_demo_house_price.price%type; | |
begin | |
apex_web_service.clear_request_headers(); | |
l_response := apex_web_service.make_rest_request( | |
p_url => 'https://www.oracle.com/webfolder/technetwork/jet/cookbook/dataCollections/dataCollections/heatmapGrid/housePriceData.json' | |
,p_http_method => 'GET' | |
); | |
l_regions := json_array_t(l_response); | |
for i in 1..l_regions.get_size() | |
loop | |
l_region_object := treat(l_regions.get(i-1) as json_object_t); | |
/* Regionを取り出して削除する。年月と値が残る */ | |
l_region := l_region_object.get_string('Region'); | |
l_region_object.remove('Region'); | |
/* 年月と値を表EBAJ_HM_DATAへ投入する */ | |
l_keys := l_region_object.get_keys(); | |
for j in 1..l_keys.count | |
loop | |
l_period := l_keys(j); | |
l_price := l_region_object.get_number(l_period); | |
insert into ebaj_demo_house_price(region, period, price) values(l_region, l_period, l_price); | |
end loop; | |
end loop; | |
commit; | |
end; |
以下はSQLコマンドでの実行結果です。
update ebaj_demo_house_price set period = 'February2013' where period = 'Febrary2013';
以上でヒートマップの表示に使うデータをデータベースにロードできました。
SELECT文を実行しデータがロードされていることを確認します。
select * from ebaj_demo_house_price;
APEXアプリケーションの作成に取り掛かります。
アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。名前はJET Scrollable Heat Mapとします。ヒートマップはデフォルトで作成されるホーム・ページに実装します。
アプリケーションが作成されます。
JET Cookbookが参照しているJavaScriptのクラスDemoArrayDataGridProviderは、そのままAPEXでも使用します。以下のファイルをダウンロードし、静的アプリケーション・ファイルとして保存します。
共有コンポーネントの静的アプリケーション・ファイルを開きます。
ファイルの作成をクリックします。
ディレクトリはdataProviderとします。コンテンツとしてDemoArrayDataGridProvider.jsを選択します。
作成をクリックします。
アップロードされたDemoArrayDataGridProvider.jsがスクリプト・エディタで開かれます。この時点ではミニファイされたファイルは作成されていません。
変更の適用をクリックします。
以上でDemoArrayDataGridProvider.jsが静的アプリケーション・ファイルとして保存されました。取消をクリックして静的アプリケーション・ファイルの一覧画面に戻ると、保存されたJavaScriptのファイルが確認できます。
静的アプリケーション・ファイルとして保存したDemoArrayDataGridProvider.jsを、Oracle JETから参照するために(相対ではなく)完全なURLが必要です。完全なURLを作るための設定を行います。
アプリケーション・アイテムAPEX_PATHおよびアプリケーションの計算を設定し、APEX_UTIL.HOST_URL('APEX_PATH')の値を参照できるようにします。
共有コンポーネントのアプリケーション・アイテムを開き、作成をクリックします。
アプリケーション・アイテムの名前はAPEX_PATHとします。その他は一番制限の厳しいデフォルトの設定のまま変更しません。
アプリケーション・アイテムの作成をクリックします。
アプリケーション・アイテムAPEX_PATHが作成されました。
作成したアプリケーション・アイテムAPEX_PATHにアプリケーションの計算を作成します。
共有コンポーネントのアプリケーションの計算を開き、作成をクリックします。
計算タイプは式、言語はPL/SQL、計算としてAPEX_UTIL.HOST_URL('APEX_PATH')を指定します。
以上で、計算の作成をクリックします。
以上で計算が作成されました。アプリケーション・アイテムAPEX_PATHより、http:またはhttps:から始まるベースURLを参照できます。
ヒートマップの表示に使用するデータを、データベースよりJSONドキュメントとして取り出すプロセスを作成します。
AjaxコールバックにプロセスGET_DATAを作成します。タイプにコードの実行を選択し、ソースのPL/SQLコードに以下を記述します。データベースにロードしたhousePrice.jsonと同じ形式のJSONドキュメントを、表EBAJ_DEMO_HOUSE_PRICEから逆に生成しています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_response clob; | |
begin | |
select json_arrayagg(nobj returning clob) into l_response from | |
( | |
select json_mergepatch(obj, '{"Region": "' || rgn || '"}') nobj | |
from ( | |
select p.region rgn, json_objectagg(p.period value p.price) obj | |
from ebaj_demo_house_price p group by p.region | |
) | |
); | |
htp.p(l_response); | |
end; |
ホーム・ページ上にScrollable Heat Mapを表示するリージョンを作成します。タイプは静的コンテンツです。
ソースのHTMLコードとして以下を記述します。JET Cookbookのdemo.htmlからoj-data-grid要素の部分を抜粋しています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<oj-data-grid | |
id="datagrid" | |
aria-label="Data Grid Heatmap Demo" | |
class="demo-data-grid" | |
data="[[dataGridProvider]]" | |
header.row.style="width:16em;" | |
header.column.class-name="demo-cell-alignment" | |
header.column.style="height:13em;width:2.5em" | |
cell.renderer="[[cellRenderer]]" | |
cell.class-name="[[setCellClass]]"> | |
<template slot="columnHeaderTemplate" data-oj-as="header"> | |
<div class="demo-content-container oj-helper-text-align-left"> | |
<oj-bind-text value="[[header.item.data]]"></oj-bind-text> | |
</div> | |
</template> | |
</oj-data-grid> |
ページ・プロパティのJavaScriptのファイルURLに以下を記述します。
[require jet]
ページ・ロード時に実行に以下を記述します。
JET Cookbookのdemo.jsそのままですが、表示に使用するデータをデータベースから取得するように変更しています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
requirejs.config({ | |
paths: { | |
'dataProvider': "&APEX_PATH!RAW.#APP_FILES#dataProvider", | |
} | |
}); | |
require(["require", "exports", "knockout", "ojs/ojbootstrap", "dataProvider/DemoArrayDataGridProvider", "ojs/ojknockout", "ojs/ojdatagrid"], function (require, exports, ko, ojbootstrap_1, DemoArrayDataGridProvider_1) { | |
"use strict"; | |
class ViewModel { | |
constructor(data) { | |
// this.jsonData = JSON.parse(jsonData); | |
this.jsonData = data; | |
this.rowHeaderProperties = ['Region']; | |
this.rowHeaders = this.rowHeaderProperties.map((prop) => { | |
return this.jsonData.map((item) => { | |
return item[prop]; | |
}); | |
}); | |
this.columnHeaders = [ | |
Object.keys(this.jsonData[0]).filter((key) => { | |
return this.rowHeaderProperties.indexOf(key) === -1; | |
}) | |
]; | |
this.data = this.jsonData.map((item) => { | |
return this.columnHeaders[0].map((header) => { | |
return { data: item[header] }; | |
}); | |
}); | |
this.dataGridProvider = ko.observable(new DemoArrayDataGridProvider_1.DemoArrayDataGridProvider({ | |
data: this.data, | |
rowHeader: this.rowHeaders, | |
columnHeader: this.columnHeaders | |
})); | |
this.columnHeaderRenderer = (headerContext) => { | |
// container div to rotate the text | |
var container = document.createElement('div'); | |
container.className = 'demo-content-container'; | |
container.appendChild(document.createTextNode(headerContext.data)); | |
return { insert: container }; | |
}; | |
this.cellRenderer = (cellContext) => { | |
// set the value as aria-label for screen reader on the cell | |
var cell = cellContext.parentElement; | |
cell.setAttribute('aria-label', cellContext.data); | |
}; | |
this.setCellClass = (cellContext) => { | |
const cell = cellContext.cell; | |
const data = cell.data; | |
if (data < -1.25) { | |
return 'oj-bg-success-30'; | |
} | |
// -1% > data >= -1.25% | |
if (data < -1) { | |
return 'oj-bg-brand-30'; | |
} | |
// -0.75% > data >= -1% | |
if (data < -0.75) { | |
return 'oj-bg-warning-30'; | |
} | |
// -0.5% > data >= -0.75% | |
if (data < -0.5) { | |
return 'oj-bg-warning-20'; | |
} | |
// -0.25% > data >= -0.5% | |
if (data < -0.25) { | |
return 'oj-bg-warning-10'; | |
} | |
// data > 2.25% | |
if (data > 2.25) { | |
return 'oj-bg-neutral-200'; | |
} | |
// 2% < data <= 2.25% | |
if (data > 2) { | |
return 'oj-bg-neutral-170'; | |
} | |
// 1.75% < data <= 2% | |
if (data > 1.75) { | |
return 'oj-bg-success-20'; | |
} | |
// 1.5% < data <= 1.75% | |
if (data > 1.5) { | |
return 'oj-bg-brand-20'; | |
} | |
// 1.25% < data <= 1.5% | |
if (data > 1.25) { | |
return 'oj-bg-info-30'; | |
} | |
// 1% < data <= 1.25% | |
if (data > 1) { | |
return 'oj-bg-info-20'; | |
} | |
// 0.75% < data <= 1% | |
if (data > 0.75) { | |
return 'oj-bg-info-10'; | |
} | |
// 0.5% < data <= 0.75% | |
if (data > 0.5) { | |
return 'oj-bg-neutral-20'; | |
} | |
// 0.25% < data <= 0.5% | |
if (data > 0.25) { | |
return 'oj-bg-neutral-10'; | |
} | |
// between -0.25 and 0.25 | |
return 'oj-bg-neutral-0'; | |
}; | |
} | |
} | |
(0, ojbootstrap_1.whenDocumentReady)().then(() => { | |
/* データベースからJSONでデータを取得する。 */ | |
apex.server.process( "GET_DATA", {}, | |
{ | |
success: (data) => { | |
/* datagridのviewModelをバインド */ | |
ko.applyBindings(new ViewModel(data), document.getElementById('datagrid')); | |
} | |
} | |
); | |
}); | |
}); |
CSSのファイルURLに以下を記述します。
#JET_CSS_DIRECTORY#redwood/oj-redwood-notag-min.css
インラインに以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.demo-data-grid { | |
width: 100%; | |
height: 27.75rem; | |
max-width: 48.875rem; | |
} | |
.demo-cell-alignment { | |
align-items: stretch; | |
} | |
.demo-content-container { | |
position: relative; | |
transform: rotate(180deg); | |
writing-mode: vertical-lr; | |
width: 9.625rem; | |
} |
今回作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/jet-scrollable-heat-map.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完