Zielsetzung
Es sollen in der S10 Anwendung Diagramme und Charts angezeigt werden. Die Daten sollen dynamisch aus dem SAP System gelesen werden. ![]() |
Implentierung
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 das 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: <input class='input' type='hidden' name='diagram_01' id='diagram_01'/> Die Daten werden dem Benutzer also nicht angezeigt, können aber am Frontend verarbeitet werden.
data: diagram_01 type string. Eine ABAP Methode, die die Daten liest, könnte sein: 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: 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: 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: <input class='input' type='hidden' name='diagram_01' id='diagram_01' onchange='Update_diagram_01(this.value)'> Die JavaScript Methode würde dann das Diagramm erstellen unter Verwendung einer bestimmten Bibliothek: 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 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 <!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> |
Komponente S10 Framework |