Oracle JETのGanttチャートを(Oracle APEXのチャートとしてではなく)、直接APEXアプリケーションに実装してみます。理論上はOracle JETのGanttチャートが提供している機能をすべて利用できるようになります。
作成したアプリケーションは以下のように動作します。
https://www.oracle.com/webfolder/technetwork/jet/jetcookbook.html?component=gantt&demo=overview
表が作成されたら、データをロードします。
いくつかのテキストやアイコンが表示されていません。CSS変数の定義の違いに影響されているのだと思われますが、原因については調べていません。
以下より実装について説明します。
Oracle JET CookbookではGanttチャートの表示に使用するデータを、rowData.jsonとdepData.jsonの2つの静的ファイルから読み込んでいます。このデータについては、データベースに表を作成してデータを投入します。
以下のDDLを実行し、表JGANTT_ROWS、JGANTT_TASKS、JGANTT_REFERENCE_OBJECTS、JGANTT_DEPENDENCIESの4つの表を作成します。
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
-- create tables | |
create table jgantt_rows ( | |
row_id varchar2(80 char) not null, | |
parent_row_id varchar2(80 char), | |
label varchar2(80 char) | |
) | |
; | |
-- table index | |
create index jgantt_rows_i1 on jgantt_rows (row_id); | |
create table jgantt_tasks ( | |
task_id varchar2(80 char) not null, | |
row_id varchar2(80 char) not null, | |
attribute_desc varchar2(80 char), | |
svg_class_name varchar2(80 char), | |
start_date date, | |
end_date date, | |
downtime_start_date date, | |
downtime_end_date date, | |
overtime_start_date date, | |
overtime_end_date date | |
) | |
; | |
-- table index | |
create index jgantt_tasks_i1 on jgantt_tasks (row_id); | |
create index jgantt_tasks_i52 on jgantt_tasks (task_id); | |
create table jgantt_reference_objects ( | |
row_id varchar2(80 char) not null, | |
id number generated by default on null as identity | |
constraint jgantt_reference_o_id_pk primary key, | |
start_date date, | |
end_date date | |
) | |
; | |
-- table index | |
create index jgantt_reference_o_i1 on jgantt_reference_objects (row_id); | |
-- create tables | |
create table jgantt_dependencies ( | |
id varchar2(80 char) not null, | |
predecessor varchar2(80 char), | |
successor varchar2(80 char) | |
) | |
; | |
-- load data |
SQLワークショップのSQLスクリプトから実行します。
ネットワーク経由でrowData.jsonを取得し、JSONをパースして表に投入します。以下のスクリプトを実行します。
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_response_json json_array_t; | |
e_load_data_failed exception; | |
/* 前後のごみ取り */ | |
l_start pls_integer; | |
l_top_row_count pls_integer; | |
/* rows object */ | |
l_row_object json_object_t; | |
procedure process_row( | |
p_row_object in json_object_t | |
,p_parent_row_id in jgantt_rows.parent_row_id%type default null | |
) | |
as | |
l_row_id jgantt_rows.row_id%type; | |
l_label jgantt_rows.label%type; | |
l_parent_row_id jgantt_rows.parent_row_id%type; | |
/* referenceObjects */ | |
l_reference_objects json_array_t; | |
l_reference_object_count pls_integer; | |
l_reference_object json_object_t; | |
r_reference_object jgantt_reference_objects%rowtype; | |
/* task object */ | |
l_tasks json_array_t; | |
l_task_count pls_integer; | |
l_task_object json_object_t; | |
r_task jgantt_tasks%rowtype; | |
/* row object */ | |
l_rows json_array_t; | |
l_row_count pls_integer; | |
l_row_object json_object_t; | |
r_reference_objects jgantt_reference_objects%rowtype; | |
begin | |
l_row_id := p_row_object.get_string('id'); | |
l_label := p_row_object.get_string('label'); | |
insert into jgantt_rows(row_id, label, parent_row_id) values(l_row_id, l_label, p_parent_row_id); | |
-- referenceObjectsの扱い。 | |
l_reference_objects := p_row_object.get_array('referenceObjects'); | |
if l_reference_objects is not null then | |
l_reference_object_count := l_reference_objects.get_size(); | |
for j in 1..l_reference_object_count | |
loop | |
l_reference_object := treat(l_reference_objects.get(j-1) as json_object_t); | |
r_reference_objects.row_id := l_row_id; | |
r_reference_objects.start_date := l_reference_object.get_timestamp('start'); | |
r_reference_objects.end_date := l_reference_object.get_timestamp('end'); | |
insert into jgantt_reference_objects values r_reference_objects; | |
end loop; | |
end if; | |
-- tasksの扱い。 | |
l_tasks := p_row_object.get_array('tasks'); | |
if l_tasks is not null then | |
l_task_count := l_tasks.get_size(); | |
for j in 1..l_task_count | |
loop | |
l_task_object := treat(l_tasks.get(j-1) as json_object_t); | |
r_task.row_id := l_row_id; | |
r_task.task_id := l_task_object.get_string('id'); | |
r_task.attribute_desc := l_task_object.get_string('attributeDesc'); | |
r_task.svg_class_name := l_task_object.get_string('svgClassName'); | |
r_task.start_date := l_task_object.get_timestamp('start'); | |
r_task.end_date := l_task_object.get_timestamp('end'); | |
r_task.downtime_start_date := l_task_object.get_timestamp('downtimeStart'); | |
r_task.downtime_end_date := l_task_object.get_timestamp('downtimeEnd'); | |
r_task.overtime_start_date := l_task_object.get_timestamp('overtimeStart'); | |
r_task.overtime_end_date := l_task_object.get_timestamp('overtimeEnd'); | |
insert into jgantt_tasks values r_task; | |
end loop; | |
end if; | |
-- rowsの扱い。 | |
l_rows := p_row_object.get_array('rows'); | |
if l_rows is not null then | |
l_row_count := l_rows.get_size(); | |
for j in 1..l_row_count | |
loop | |
l_row_object := treat(l_rows.get(j-1) as json_object_t); | |
process_row(l_row_object, l_row_id); | |
end loop; | |
end if; | |
end process_row; | |
begin | |
apex_web_service.set_request_headers('Content-Type','application/json'); | |
l_response := apex_web_service.make_rest_request( | |
p_url => 'https://www.oracle.com/webfolder/technetwork/jet/cookbook/dataVisualizations/gantt/overview/rowData.json' | |
,p_http_method => 'GET' | |
); | |
if apex_web_service.g_status_code <> 200 then | |
raise e_load_data_failed; | |
end if; | |
l_start := instr(l_response, '['); | |
l_response := substr(l_response, l_start); | |
-- dbms_output.put_line(substr(l_response,1,100)); | |
l_response_json := json_array_t(l_response); | |
l_top_row_count := l_response_json.get_size(); | |
for i in 1..l_top_row_count | |
loop | |
l_row_object := treat(l_response_json.get(i-1) as json_object_t); | |
process_row(l_row_object); | |
end loop; | |
end; |
SQLワークショップのSQLコマンドから実行します。
同様にdepData.jsonを取得し、表にロードします。
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_response_json json_array_t; | |
e_load_data_failed exception; | |
/* 前後のごみ取り */ | |
l_start pls_integer; | |
l_count pls_integer; | |
/* rows object */ | |
l_object json_object_t; | |
r_dependencies jgantt_dependencies%rowtype; | |
begin | |
apex_web_service.set_request_headers('Content-Type','application/json'); | |
l_response := apex_web_service.make_rest_request( | |
p_url => 'https://www.oracle.com/webfolder/technetwork/jet/cookbook/dataVisualizations/gantt/overview/depData.json' | |
,p_http_method => 'GET' | |
); | |
if apex_web_service.g_status_code <> 200 then | |
raise e_load_data_failed; | |
end if; | |
l_start := instr(l_response, '['); | |
l_response := substr(l_response, l_start); | |
-- dbms_output.put_line(substr(l_response,1,100)); | |
l_response_json := json_array_t(l_response); | |
l_count := l_response_json.get_size(); | |
for i in 1..l_count | |
loop | |
l_object := treat(l_response_json.get(i-1) as json_object_t); | |
r_dependencies.id := l_object.get_string('id'); | |
r_dependencies.predecessor := l_object.get_string('predecessor'); | |
r_dependencies.successor := l_object.get_string('successor'); | |
insert into jgantt_dependencies values r_dependencies; | |
end loop; | |
end; |
モジュール・ベース・パスを/jgantt/、URLテンプレートをoverview、メソッドはGETを指定して、リソース・ハンドラを作成します。
ソース・タイプとしてPL/SQLを選択し、ソースに以下を記述します。
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_json json_object_t; | |
l_response blob; | |
/* rowData.jsonの生成に使う */ | |
l_rows json_array_t; | |
l_row_object json_object_t; | |
/* depData.jsonの生成に使う */ | |
l_deps json_array_t; | |
l_dep json_object_t; | |
/* | |
* rowオブジェクトを生成する | |
*/ | |
function get_row_object( | |
p_row_id in jgantt_rows.row_id%type | |
) | |
return json_object_t | |
as | |
l_row_object json_object_t; | |
/* referenceObjects */ | |
l_reference_objects json_array_t; | |
l_reference_object json_object_t; | |
/* tasks */ | |
l_tasks json_array_t; | |
l_task json_object_t; | |
/* rows */ | |
l_rows json_array_t; | |
begin | |
l_row_object := json_object_t(); | |
/* row_idでの検索なので、選択されるのは一行だけ */ | |
for r in (select * from jgantt_rows where row_id = p_row_id) | |
loop | |
l_row_object.put('id', r.row_id); | |
l_row_object.put('label', r.label); | |
/* referenceObjectsの生成 */ | |
l_reference_objects := json_array_t(); | |
for e in (select * from jgantt_reference_objects where row_id = r.row_id) | |
loop | |
l_reference_object := json_object_t(); | |
if e.start_date is not null then l_reference_object.put('start', e.start_date); end if; | |
if e.end_date is not null then l_reference_object.put('end', e.end_date); end if; | |
l_reference_objects.append(l_reference_object); | |
end loop; | |
if l_reference_objects.get_size() > 0 then | |
l_row_object.put('referenceObjects', l_reference_objects); | |
end if; | |
/* tasksの生成 */ | |
l_tasks := json_array_t(); | |
for t in (select * from jgantt_tasks where row_id = r.row_id) | |
loop | |
l_task := json_object_t(); | |
l_task.put('id', t.task_id); | |
if t.attribute_desc is not null then l_task.put('attributeDesc', t.attribute_desc); end if; | |
if t.svg_class_name is not null then l_task.put('svgClassName', t.svg_class_name); end if; | |
if t.start_date is not null then l_task.put('start', t.start_date); end if; | |
if t.end_date is not null then l_task.put('end', t.end_date); end if; | |
if t.downtime_start_date is not null then l_task.put('downtimeStart', t.downtime_start_date); end if; | |
if t.downtime_end_date is not null then l_task.put('downtimeEnd', t.downtime_end_date); end if; | |
if t.overtime_start_date is not null then l_task.put('overtimeStart', t.downtime_start_date); end if; | |
if t.overtime_end_date is not null then l_task.put('overtimeEnd', t.downtime_end_date); end if; | |
l_tasks.append(l_task); | |
end loop; | |
if l_tasks.get_size() > 0 then | |
l_row_object.put('tasks', l_tasks); | |
end if; | |
/* 子供のrowsの生成 */ | |
l_rows := json_array_t(); | |
for c in (select row_id from jgantt_rows where parent_row_id = r.row_id) | |
loop | |
l_rows.append(get_row_object(c.row_id)); | |
end loop; | |
if l_rows.get_size() > 0 then | |
l_row_object.put('rows', l_rows); | |
end if; | |
end loop; | |
return l_row_object; | |
end get_row_object; | |
begin | |
/* | |
* rowData.jsonに相当するデータを生成する | |
*/ | |
l_rows := json_array_t(); | |
for c in (select row_id from jgantt_rows where parent_row_id is null) | |
loop | |
l_rows.append(get_row_object(c.row_id)); | |
end loop; | |
/* | |
* depData.jsonに相当するデータを生成する。 | |
*/ | |
l_deps := json_array_t(); | |
for c in (select * from jgantt_dependencies) | |
loop | |
l_dep := json_object_t(); | |
l_dep.put('id', c.id); | |
l_dep.put('predecessor', c.predecessor); | |
l_dep.put('successor', c.successor); | |
l_deps.append(l_dep); | |
end loop; | |
l_response_json := json_object_t(); | |
l_response_json.put('rowData', l_rows); | |
l_response_json.put('depData', l_deps); | |
l_response := l_response_json.to_blob(); | |
owa_util.mime_header('application/json'); | |
wpg_docload.download_file(l_response); | |
end; |
APEXアプリケーションからは完全なURLを呼び出してデータを取得するため、このURLを記録しておきます。
Breadcrumb BarにあるリージョンJET Native Ganttを削除します。その後にBodyに新規にリージョンを作成します。
識別のタイトルをGantt、タイプととして静的コンテンツを選択します。ソースのHTMLコードに以下を記述します。これはOracle JET Cookbookのdemo.htmlとほぼ同じで、<head>...</head>を取り除いています。
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
<html lang="en-us" style="height:100%;" dir="ltr"> | |
<body class="demo-disable-bg-image"> | |
<div id="sampleDemo" class="demo-padding demo-container"> | |
<div id="componentDemoContent" style="width: 1px; min-width: 100%;"> | |
<div id="container"> | |
<div class="oj-panel oj-bg-info-30 oj-sm-margin-4x-bottom"> | |
<h2 id="h1" class="oj-typography-subheading-md">Options To Control The Gantt Below</h2> | |
<oj-form-layout user-assistance-density="reflow" max-columns="1" direction="row"> | |
<oj-checkboxset | |
id="task-elements" | |
class="oj-choice-direction-row" | |
label-hint="Task elements to show" | |
value="[[taskElementsDetails]]" | |
on-value-changed="[[handleTaskElementsSettings]]"> | |
<oj-option value="attribute">Attribute</oj-option> | |
<oj-option value="overtime">Overtime</oj-option> | |
<oj-option value="downtime">Downtime</oj-option> | |
</oj-checkboxset> | |
<oj-checkboxset | |
id="toggles" | |
class="oj-choice-direction-row" | |
label-hint="Controls" | |
value="[[togglesDetails]]" | |
on-value-changed="[[handleTogglesSettings]]"> | |
<oj-option value="timeCursor">Time Cursor</oj-option> | |
<oj-option value="zooming">Zooming</oj-option> | |
</oj-checkboxset> | |
</oj-form-layout> | |
</div> | |
<oj-gantt | |
id="gantt" | |
start="[[projectStartDate.toISOString()]]" | |
end="[[projectEndDate.toISOString()]]" | |
viewport-start="[[viewportStart.toISOString()]]" | |
viewport-end="[[viewportEnd.toISOString()]]" | |
gridlines.vertical="visible" | |
expanded="{{expanded}}" | |
zooming="[[zooming]]" | |
time-cursor="[[timeCursor]]" | |
row-axis.rendered="on" | |
row-axis.width="210px" | |
major-axis.scale="days" | |
major-axis.converter.days="[[dateConverter]]" | |
major-axis.zoom-order='[[ ["weeks","days",custom8HrScale,"hours"] ]]' | |
minor-axis.scale="[[custom8HrScale]]" | |
minor-axis.zoom-order='[[ ["weeks","days",custom8HrScale,"hours"] ]]' | |
selection-mode="multiple" | |
selection-behavior="highlightDependencies" | |
dnd.move.tasks="enabled" | |
on-oj-move="[[handleMove]]" | |
task-defaults.resizable="enabled" | |
on-oj-resize="[[handleResize]]" | |
task-aggregation="on" | |
dependency-line-shape="straight" | |
reference-objects="[[referenceObjects]]" | |
row-data="[[dataProvider]]" | |
dependency-data="[[dependenciesDataProvider]]" | |
:aria-label='[["Gantt Chart. Current date is " + currentDateFormatted ]]' | |
class="demo-gantt"> | |
<template slot="rowMappingTemplate" data-oj-as="row"> | |
<oj-gantt-row | |
reference-objects="[[row.data.referenceObjects]]" | |
tasks="[[row.data.tasks]]" | |
label="[[row.data.label]]" | |
short-desc="[[getRowDesc(row)]]"></oj-gantt-row> | |
</template> | |
<template slot="taskMappingTemplate" data-oj-as="task"> | |
<oj-gantt-task | |
task-id="[[task.data.id]]" | |
start="[[task.data.start]]" | |
end="[[task.data.end]]" | |
height="[[task.data.svgClassName === 'demo-gantt-task-hold' ? 12 : null]]" | |
border-radius="[[task.data.svgClassName === 'demo-gantt-task-hold' ? '0' : null]]" | |
attribute.rendered="[[!showAttribute() || task.data.svgClassName === 'demo-gantt-task-hold' ? 'off' : 'on']]" | |
attribute.short-desc="Attribute Description" | |
downtime.start="[[showDowntime() ? task.data.downtimeStart : null]]" | |
downtime.end="[[showDowntime() ? task.data.downtimeEnd : null]]" | |
overtime.start="[[showOvertime() ? task.data.overtimeStart : null]]" | |
overtime.end="[[showOvertime() ? task.data.overtimeEnd : null]]" | |
svg-class-name="[[task.data.svgClassName]]"></oj-gantt-task> | |
</template> | |
<template slot="dependencyTemplate" data-oj-as="dependency"> | |
<oj-gantt-dependency | |
predecessor-task-id="[[dependency.data.predecessor]]" | |
successor-task-id="[[dependency.data.successor]]"></oj-gantt-dependency> | |
</template> | |
<template slot="referenceObjectMappingTemplate" data-oj-as="ref"> | |
<oj-gantt-reference-object | |
start="[[ref.data.start]]" | |
end="[[ref.data.end]]"></oj-gantt-reference-object> | |
</template> | |
<template slot="rowAxisLabelTemplate" data-oj-as="rowAxisLabel"> | |
<svg class="demo-gantt-row-label"> | |
<foreignObject | |
:x="[[getRowLabelX(rowAxisLabel.maxWidth)]]" | |
y="0" | |
:width="[[rowAxisLabel.maxWidth]]" | |
:height="[[rowAxisLabel.maxHeight]]"> | |
<div class="oj-flex oj-sm-align-items-center demo-full-size"> | |
<span :class='[[ {"oj-typography-semi-bold": !rowAxisLabel.leaf} ]]'> | |
<oj-bind-text value="[[rowAxisLabel.rowData.label]]"></oj-bind-text> | |
</span> | |
<oj-bind-if test="[[!rowAxisLabel.leaf && !expanded().has(rowAxisLabel.rowData.id)]]"> | |
<span class="oj-badge oj-badge-success oj-badge-subtle oj-sm-margin-2x-start"> | |
<oj-bind-text value="[['+' + rowAxisLabel.data.rows.length]]"></oj-bind-text> | |
</span> | |
</oj-bind-if> | |
<oj-bind-if test="[[!rowAxisLabel.leaf]]"> | |
<span | |
class="oj-icon-color-danger oj-ux-ico-triangle-up-s oj-ux-icon-size-2x oj-sm-margin-2x-start"></span> | |
</oj-bind-if> | |
</div> | |
</foreignObject> | |
</svg> | |
</template> | |
</oj-gantt> | |
<br /> | |
<p> | |
Task Action: | |
<span id="results" class="italic oj-typography-body-md"> | |
<oj-bind-text value="[[dndAction]]"></oj-bind-text> | |
</span> | |
</p> | |
</div> | |
</div> | |
</div> | |
</body> | |
</html> |
外観のテンプレートとして、Blank with Attributes (No Grid)を選択しています。
ページ・プロパティのJavaScriptのファイルURLに以下を記述します。
[require jet]
ファンクションおよびグローバル変数の宣言に以下を記述します。
var view;
ページ・ロード時に実行に以下を記述します。
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
require(["require", "exports", "knockout", "ojs/ojbootstrap", "ojs/ojarraydataprovider", "ojs/ojarraytreedataprovider", "ojs/ojconverter-datetime", "ojs/ojknockout-keyset", "ojs/ojknockout", "ojs/ojgantt", "ojs/ojcheckboxset", "ojs/ojformlayout"], function (require, exports, ko, ojbootstrap_1, ArrayDataProvider, ArrayTreeDataProvider, ojconverter_datetime_1, ojknockout_keyset_1) { | |
"use strict"; | |
class DemoCustomScaleNHr { | |
constructor(N) { | |
this.converter = new ojconverter_datetime_1.IntlDateTimeConverter({ | |
hour: '2-digit', | |
hour12: true | |
}); | |
this.hour = 60 * 60 * 1000; | |
this.name = `${N}hr`; | |
this.N = N; | |
} | |
formatter(date) { | |
return this.converter.format(date); | |
} | |
getNextDate(date) { | |
return new Date(new Date(date).getTime() + this.N * this.hour).toISOString(); | |
} | |
getPreviousDate(date) { | |
const d = new Date(date); | |
d.setHours(Math.floor(d.getHours() / this.N) * this.N, 0, 0, 0); | |
return d.toISOString(); | |
} | |
} | |
class ViewModel { | |
constructor(data) { | |
this.dataProvider = new ArrayTreeDataProvider(data.rowData, { | |
keyAttributes: 'id', | |
childrenAttribute: 'rows' | |
}); | |
this.dependenciesDataProvider = new ArrayDataProvider(data.depData, { | |
keyAttributes: 'id' | |
}); | |
this.taskElementsDetails = []; | |
this.togglesDetails = []; | |
this.showAttribute = ko.observable(false); | |
this.showOvertime = ko.observable(false); | |
this.showDowntime = ko.observable(false); | |
this.timeCursor = ko.observable('off'); | |
this.zooming = ko.observable('off'); | |
this.dndAction = ko.observable('(Move or Resize a Task)'); | |
this.handleTaskElementsSettings = (event) => { | |
this.taskElementsDetails = event.detail.value; | |
this.handleSettings(this.taskElementsDetails.concat(this.togglesDetails)); | |
}; | |
this.handleTogglesSettings = (event) => { | |
this.togglesDetails = event.detail.value; | |
this.handleSettings(this.taskElementsDetails.concat(this.togglesDetails)); | |
}; | |
this.handleSettings = (details) => { | |
this.showAttribute(details.indexOf('attribute') !== -1); | |
this.showOvertime(details.indexOf('overtime') !== -1); | |
this.showDowntime(details.indexOf('downtime') !== -1); | |
this.timeCursor(details.indexOf('timeCursor') !== -1 ? 'on' : 'off'); | |
this.zooming(details.indexOf('zooming') !== -1 ? 'on' : 'off'); | |
}; | |
this.handleMove = (event) => { | |
const taskContexts = event.detail.taskContexts; | |
const rowContext = event.detail.rowContext; | |
const dropDate = event.detail.value; | |
this.dndAction(`${taskContexts.length} task(s) dropped on ${rowContext.rowData.label} at ${dropDate}`); | |
}; | |
this.handleResize = (event) => { | |
const taskContexts = event.detail.taskContexts; | |
const dropDate = event.detail.value; | |
this.dndAction(`${taskContexts.length} task(s) resized to ${dropDate}`); | |
}; | |
this.expanded = new ojknockout_keyset_1.ObservableKeySet().add(['Mixer A', 'Packaging A']); | |
this.projectStartDate = new Date('2020-10-01T00:00:00'); | |
this.projectEndDate = new Date('2020-10-31T00:00:00'); | |
// 8 hours scale | |
this.custom8HrScale = new DemoCustomScaleNHr(8); | |
// Date converter | |
this.dateConverter = new ojconverter_datetime_1.IntlDateTimeConverter({ | |
formatType: 'date', | |
dateFormat: 'long' | |
}); | |
this.timeConverter = new ojconverter_datetime_1.IntlDateTimeConverter({ | |
formatType: 'time' | |
}); | |
this.currentDate = new Date('Oct 03, 2020, 17:00:00'); | |
this.currentDateString = this.currentDate.toISOString(); | |
this.currentDateFormatted = this.dateConverter.format(this.currentDateString); | |
// set viewport to cover two days before and after | |
this.day = 1000 * 60 * 60 * 24; | |
this.viewportStart = new Date('Oct 03, 2020'); | |
this.viewportEnd = new Date(this.viewportStart.getTime() + 3 * this.day); | |
this.referenceObjects = [ | |
{ | |
value: this.currentDateString, | |
label: this.timeConverter.format(this.currentDateString), | |
svgClassName: 'demo-current-time-indicator' | |
} | |
]; | |
this.getRowDesc = (row) => { | |
const desc = [row.data.label]; | |
if (row.data.rows) { | |
desc.push(`${row.data.rows.length} siblings`); | |
} | |
desc.push('1 issue'); | |
return desc.join(', '); | |
}; | |
// Helper function to get appropriate row label x position depending on document reading direction | |
this.getRowLabelX = (rowAxisWidth) => { | |
const dir = document.documentElement.getAttribute('dir'); | |
return dir === 'ltr' ? '0' : -rowAxisWidth; | |
}; | |
} | |
} | |
(0, ojbootstrap_1.whenDocumentReady)().then(() => { | |
fetch("&G_DATA_URL!RAW.") | |
.then((response) => response.json()) | |
.then( (data) => { | |
view = new ViewModel(data); | |
ko.applyBindings(view, document.getElementById('container')); | |
} | |
); | |
}); | |
}); |
CSSのファイルURLに以下を記述します。
#JET_CSS_DIRECTORY#redwood/oj-redwood-notag-min.css
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-gantt { | |
width: 100%; | |
height: 25rem; | |
} | |
.demo-full-size { | |
width: 100%; | |
height: 100%; | |
} | |
.demo-gantt .demo-gantt-row-label { | |
overflow: visible; | |
} | |
.demo-current-time-indicator { | |
stroke: var(--oj-dvt-danger-color); | |
} | |
.demo-gantt-task { | |
fill: rgb(var(--oj-palette-neutral-rgb-0)); | |
} | |
.demo-gantt-task-emphasis-low { | |
fill: rgba(var(--oj-palette-dvt-rgb-2), 0.2); | |
} | |
.demo-gantt-task-emphasis-high { | |
fill: rgb(var(--oj-palette-dvt-rgb-7)); | |
} | |
.demo-gantt-task-hold { | |
fill: rgb(var(--oj-palette-neutral-rgb-130)); | |
} |
JSONデータを返すRESTサービスのURLは、アプリケーション定義に置換文字列G_DATA_URLとして定義します。
以上でアプリケーションは完成です。アプリケーションを実行すると、記事の先頭のGIF動画のように動作します。
Oracle JET自体はオープンソースのJavaScriptライブラリであり、利用に当たって費用が発生しないかわりに、開発の主体であるオラクルからサポートを受けることもできません。Oracle APEXでは、APEXが使用している範囲であればOracle JETについてもSRを受け付けますが、Oracle JETをサポートしているわけではありません。
世の中には、Ganttチャートを実装したJavaScriptライブラリがいくつがあります。例えばAnyChartのAnyGanttやHighchartsのHighcarts Ganttなどです。これらは有償ですが、技術サポートが含まれています。単純に無料で使えるのでOracle JETを使って実装しようと考える前に、サポートが付いている商用のライブラリの使用を検討しても良いかと思います。
今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/jet-native-gantt.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完