Goal
Diagrams and charts are to be displayed in the S10 application. The data should be read dynamically from the SAP system.



Implementation
The S10 Framework itself does not offer any standard functionality for displaying diagrams. However, there is a large number of JavaScript libraries that can be easily integrated into an S10 application. The transfer of the data from the SAP system to the library is decisive here. This can be done as follows:
 
The data required for a diagram are obtained in an ABAP method and stored in an S10 field. This field can e.g. be present as a hidden input field in the S10 application, i.e. in the HTML code:

<input class='input' type='hidden' name='diagram_01' id='diagram_01'/>

The data is not displayed to the user, but can be processed at the front end.
In an ABAP class there is a corresponding field, that means with the same name:


data: diagram_01 type string.

An ABAP method that reads the data could be:

method build_diagram_01.

diagram_01 = 'Values for the diagram e.g. title, series of numbers, labels etc.'

Endmethod.

As soon as the data has been read in an ABAP method, it is available in the HTML interface and can be processed with a JavaScript method and transferred to a library for generating diagrams:
function CreateDiagram()
{
	var values = document.getElementById('diagram_01').value;
	
	myDiagramLibrary.create(values);
}


There are two mechanisms for regenerating diagrams dynamically:
 
1. So-called build methods can be defined in an ABAP class, which are called when other fields change.

Example:
If the customer number selected by the user changes, the data for the diagram is read again:
build_diagram_01
        importing
          kunnr      type kunnr
        exporting
          diagram_0 type string,


2. An onchange event can be defined in the HTML code for an input field, so that if the data is changed, a JavaScript method is automatically called to update the diagrams:
<input class='input' type='hidden' name='diagram_01' id='diagram_01'
    onchange='Update_diagram_01(this.value)'>

The JavaScript method would then create the diagram using a certain library:
function update_diagram_01(v)
{
    // Create diagram
    // ...
       
}

In the simplest case, one would simply pass a list of numbers, e.g. separated by a semicolon. However, other data structures are also conceivable, e.g. a JSON-formatted text.
Example
In the following example, two simple diagrams are filled with random data that are read in an ABAP method. You can use the example as a basis for your own implementations:




ABAP Progrmm: /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.

HTML View: diagram_class.start.html
<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <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>Examples of diagrams with S10</b>
    </div>

    <div class="toolbar">

        <button type="button" class="toolbarbutton" onclick="S10Apply('generate_random');">
           generate random data
        </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);">
                Example with bar chart
            </td>
            <td id="tab_example_2" class="tab" onclick="S10ActivateTab(this);">

                Example with pie chart

            </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">Diagram for customer</span><br>

                    <!-- User can manpulate data in this field -->
                    <span class="label">Data for chart</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">Material number</span><br>
                    <input class='input' type='text' name='matnr' id="matnr" /> <br>

                    <span style="display:none" id="chart-2-title">Pie chart for 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>

Component: S10 Framework