Zielsetzung Es sollen in der S10 Anwendung Diagramme und Charts angezeigt werden. Die Daten sollen dynamisch aus dem SAP System gelesen werden.
Implementierung Das S10 Framework bietet selbst keine Standardfunktionalität für die Anzeige von Diagrammen an. Es existiert jedoch eine große Anzahl an JavaScript Bibliotheken, die sehr einfach in eine S10 Anwendung eingebunden werden können. Entscheidend ist hierbei die Übergabe der Daten aus dem SAP System an die Bibliothek. Dies kann folgendermaßen geschehen:
Die benötigten Daten für ein Diagramm werden in einer ABAP Methode besorgt und in einem S10 Feld gespeichert. Dieses Feld kann z.B. als verstecktes Eingabefeld in die S10 Anwendung, also im HTML Code, vorhanden sein:
HTML
Die Daten werden dem Benutzer also nicht angezeigt, können aber am Frontend verarbeitet werden.
In einer ABAP Klasse existiert ein entsprechendes Feld, das heißt mit dem gleichen Namen:
ABAP
data: diagram_01 type string.
Eine ABAP Methode, die die Daten liest, könnte sein:
ABAP
method build_diagram_01.
diagram_01 = 'Werte für das Diagramm z.B. Titel, Zahlenreihen, Labels etc..'
Endmethod.
Sobald die Daten in einer ABAP Methode gelesen wurden, stehen sie in der HTML Oberfläche zur Verfügung und können mit eine JavaScript Methode verarbeitet und an eine Bibliothek zur Generierung von Diagrammen übergeben werden:
JavaScript
function CreateDiagram()
{
var values = document.getElementById('diagram_01').value;
myDiagramLibrary.create(values);
}
Um Diagramme dynamisch neu generieren zu lassen, existieren zwei Mechanismen:
1. In einer ABAP Klasse können sog. Build-Methoden definiert werden, die aufgerufen werden, wenn sich andere Felder ändern.
Beispiel:
Ändert sich die vom Benutzer ausgewählte Kundennummer, werden die Daten für das Diagramm neu gelesen:
ABAP
build_diagram_01
importing
kunnr type kunnr
exporting
diagram_0 type string,
2. Für ein Eingabefeld kann im HTML Code ein onchange-Ereignis definiert werden, so dass bei Änderungen der Daten automatisch eine JavaScript Methode aufgerufen wird, um die Diagramme zu aktualisieren:
HTML
Die JavaScript Methode würde dann das Diagramm erstellen unter Verwendung einer bestimmten Bibliothek:
JavaScript
function update_diagram_01(v)
{
// Create diagram
// ...
}
Im einfachsten Fall würde man einfach eine Liste mit Zahlen übergeben, z.B. durch Semikolon getrennt. Denkbar sind aber auch andere Datenstrukturen, z.B. ein JSON-formatierter Text.
Beispiel Im folgenden Beispiel werden zwei einfache Diagramme mit zufälligen Daten gefüllt, die in einer ABAP Methode gelesen werden. Sie können das Beispiel als Grundlage für eigene Implementierungen verwenden:
ABAP Programm: /s10/diagram_examples
ABAP
program /s10/diagram_examples.
* diagram examples
class diagram_class definition inheriting from /s10/any.
public section.
* project path (textpool, screens)
constants:
projectpath type string value 'sap/bc/bsp/s10/diagram'.
data:
diagram_01 type string,
diagram_02 type string,
kunnr type kna1-kunnr,
matnr type mara-matnr.
methods:
* Method to be run when screen 'start' becomes active
on_init_start,
* generate some random date
generate_random,
* Generate some data for diagram no. 1
build_diagram_01
importing
kunnr type kna1-kunnr
exporting
diagram_01 type string,
*Generate data for another diagram
build_diagram_02
importing
matnr type mara-matnr
exporting
diagram_02 type string.
endclass.
class diagram_class implementation.
method on_init_start.
if kunnr is initial.
kunnr = 'K1032'.
endif.
if matnr is initial.
matnr = '97'.
endif.
endmethod.
method generate_random.
data:
o_rand type ref to cl_abap_random_int,
seed type i.
" random number sequence.
seed = cl_abap_random=>seed( ).
cl_abap_random_int=>create(
exporting
seed = conv i( sy-uzeit )
min = 5
max = 150
receiving
prng = o_rand
).
diagram_01 = 'A;' && o_rand->get_next( ) && ';' && o_rand->get_next( ).
diagram_01 = diagram_01
&& '|B;' && o_rand->get_next( )
&& ';' && o_rand->get_next( ).
diagram_01 = diagram_01
&& '|C;' && o_rand->get_next( )
&& ';' && o_rand->get_next( ).
diagram_01 = diagram_01
&& '|D;' && o_rand->get_next( )
&& ';' && o_rand->get_next( ).
diagram_01 = diagram_01
&& '|E;' && o_rand->get_next( )
&& ';' && o_rand->get_next( ).
diagram_02 = 'A;' && o_rand->get_next( ) .
diagram_02 = diagram_02
&& '|B;' && o_rand->get_next( ) .
diagram_02 = diagram_02
&& '|C;' && o_rand->get_next( ) .
diagram_02 = diagram_02
&& '|D;' && o_rand->get_next( ).
endmethod.
method build_diagram_01.
* Just for testing purposes: labe1;value1;value2|label2;value1;value2 etc.
diagram_01 = 'A;1;2|B;2;4|C;3;6|D;4;8|E;5;10'.
endmethod.
method build_diagram_02.
diagram_02 = 'A;25|B;25|C;40|D;10'.
endmethod.
endclass.
* main class for this application
class main definition inheriting from /s10/any.
public section.
data:
mandt type t000-mandt, "client
mtext type t000-mtext. "client text
* We use another object of class diagram_class
data: mydiagram_class type ref to diagram_class.
methods:
logon.
endclass.
class main implementation.
* logon user
method logon.
* set S10 license
data: mydemolicense type string.
mydemolicense = |Synactive GmbH demo license number=100|.
mydemolicense = mydemolicense
&& | role=s10demo_role maxusers=10 signature=821.126.87.7|.
s10setlicense( mydemolicense ).
* read client text and city from client table T000
select single mandt mtext
into (mandt,mtext)
from t000
where mandt = sy-mandt.
* create our object
create object mydiagram_class.
* show start-screen, mydiagram_class will become the active object
mydiagram_class->s10nextscreen( 'start').
endmethod.
endclass.
Der HTML View: diagram_class.start.html
HTML
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=400">
<link rel='stylesheet' type='text/css' href='../../../style/s10.style.css'>
<link rel='stylesheet' type='text/css' href='../../../style/custom.style.css'>
<script src='../../synactiveS10/synactiveS10.java.js'></script>
<!-- ChartJS is an open-source library for creating charts-->
<script src='../../../scripts/Chart.js'></script>
<!-- We can use predefined color-schemes for the colors for the diagrams-->
<script src="../../../scripts/chartjs-plugin-colorschemes.js"></script>
<title>Diagramme</title>
</head>
<body style="width: 100%; margin: 0px;
padding: 0px;" onload='init();' class="colorscheme9">
<div class="headerarea" style="width: 100%; padding: 10px;">
<b>Beispiele für Diagramme mit S10</b>
</div>
<div class="toolbar">
<button type="button" class="toolbarbutton"
onclick="S10Apply('generate_random');">
zufällige Daten generieren
</button>
<button type="button" class="toolbarbutton"
style="float: right;" onclick="S10Logoff();">
Abmelden
</button>
</div>
<table class="tabstrip">
<tr>
<td id="tab_example_1" class="tabactive"
onclick="S10ActivateTab(this);">
Beispiel mit Balkendiagramm
</td>
<td id="tab_example_2" class="tab"
onclick="S10ActivateTab(this);">
Beispiel mit Kuchendiagramm
</td>
</table>
<div class="tabpageactive" id="tab_example_1.area">
<table style="width: 100%; padding: 15px;
border-radius: 12px; background: #f1f5fb;">
<tr>
<td>
<span class="label">
Kundennummer</span><br>
<input class='input' type='text'
name='kunnr' id='kunnr' />
<span class="output" name="kunnr@text"
id="kunnr_name"></span><br>
<!-- Hidden title for the diagram, put in via javascript later -->
<span style="display:none" id="chart-1-title">
Diagramm für Kunde</span><br>
<!-- User can manpulate data in this field -->
<span class="label">Daten für Diagramm</span><br>
<input class='input' type='text' name='diagram_01'
id='diagram_01' style="width:300px;"
onchange="updateChart(this.value,'chart-area-1','bar',
document.getElementById('chart-1-title').innerHTML + ' '
+ document.getElementById('kunnr_name').innerHTML)" />
<br>
<!-- Hidden field, filled by ABAP method via S10 Framework,
starts JavaScript on value change -->
<input class='input' type='hidden'
name='diagram_01' id='diagram_01_hidden'
onchange="updateChart(this.value,'chart-area-1','bar',
document.getElementById('chart-1-title').innerHTML + ' '
+ document.getElementById('kunnr_name').innerHTML)" /> <br>
</td>
</tr>
<tr>
<td>
<!-- Target area for the generated diagram-->
<canvas id="chart-area-1" style="width:100vw;height:50vh"></canvas>
</td>
</tr>
</table>
</div>
<!-- Almost same coding, but other chart-type (pie)-->
<div class="tabpage" id="tab_example_2.area">
<table style="width: 100%; padding: 15px;
border-radius: 12px; background: #f1f5fb;">
<tr>
<td>
<span class="label">Materialnummer</span><br>
<input class='input' type='text' name='matnr' id="matnr" /> <br>
<span style="display:none" id="chart-2-title">
Kuchendiagramm für Material</span><br>
<span class="label">Daten für Diagramm</span><br>
<input class='input' type='text'
name='diagram_02' id='diagram_02' style="width:300px;"
onchange="updateChart(this.value,'chart-area-2','pie',
document.getElementById('chart-2-title').innerHTML + ' '
+ document.getElementById('matnr').value)" />
<br>
<input class='input' type='hidden'
name='diagram_02' id='diagram_02_hidden'
onchange="updateChart(this.value,'chart-area-2','pie',
document.getElementById('chart-2-title').innerHTML + ' '
+ document.getElementById('matnr').value)" />
<br>
</td>
</tr>
<tr>
<td>
<canvas id="chart-area-2"
style="width:100vw;height:50vh"></canvas>
</td>
</tr>
</table>
</div>
<script>
var myCharts = {};
function updateChart(v, chartArea, barType, title) {
// Extract values from string, format depends on own implementation
// Here: label1;value1;value2|label2;value1;value2 ...
a = v.split("|");
var mydata = [];
var mydata2 = [];
var mylabels = [];
var dataset1 = [];
var dataset2 = [];
for (var i = 0; i < a.length; i++) {
mylabels.push(a[i].split(";")[0]);
mydata.push(a[i].split(";")[1]);
if (a[i].split(";").length >= 3) {
mydata2.push(a[i].split(";")[2]);
}
}
// Dataset for previous year
dataset1 = {
label: new Date().getFullYear() - 1,
borderWidth: 1,
data: mydata,
};
// Dataset for current year
dataset2 = {
label: new Date().getFullYear(),
borderWidth: 1,
data: mydata2,
};
var myChartData = {
labels: mylabels,
datasets: [
dataset1, dataset2
]
};
var ctx = document.getElementById(chartArea).getContext('2d');
// Create chart-boject only one time and save it to an array
if (typeof myCharts[chartArea] == 'undefined') {
myCharts[chartArea] = new Chart(ctx, {
type: barType,
data: myChartData,
options: {
plugins: {
colorschemes: {
scheme: 'brewer.Paired12'
}
},
responsive: true,
legend: {
position: 'top',
},
title: {
display: true,
text: title
}
}
});
}
else {
myCharts[chartArea].data = myChartData;
myCharts[chartArea].type = barType;
myCharts[chartArea].options = {
plugins: {
colorschemes: {
scheme: 'brewer.Paired12'
}
},
responsive: true,
legend: {
position: 'top',
},
title: {
display: true,
text: title
}
}
}
myCharts[chartArea].update();
}
</script>
</body>
</html>