Übersicht
Dialogteile anderer ABAP Klassen können Sie als "iframe" (inline frame) in Ihre HTML-Seite einbetten. Sie benötigen dazu in Ihrer ABAP Klasse nur ein Attribut mit einer Objektreferenz auf die externe Klasse. Durch das Einbetten des iframe wird nicht nur die Visualisierung in HTML inkludiert, sondern die externe Klasse wird aktiv zur Darstellung und Verarbeitung der Daten aus dem eingebetteten Teil.

iframes sind Teil des HTML5 Standards; sie sind in allen gängigen Browsern unterstützt. Solange die eingebetteten Teile aus der gleichen Quelle stammen, in unserem Fall also das gleiche SAP-System, treten bei der Verwendung der iframes keine Sicherheitsprobleme mit der "same origin policy" auf, mit der Browser den wechselseitigen Zugriff über JavaScript im Fall von iframes unterschiedlicher Herkunft unterbinden.

Beispiel
In einer Seite mit verschiedenen Informationen zu einem Kunden möchten Sie die in der SAP-Kundenhierarchie abgelegten Unternehmensbeziehungen als Diagramm einblenden. Zwischen die Abschnitte "Beziehungen" und "Preise, Rechnung" soll die Hierarchiedarstellung eingefügt werden:



Statt in Ihrer Anwendung selbst die Kundenhierarchie zu lesen und daraus ein Diagramm aufzubauen, verwenden Sie einen bestehenden Baustein und betten ihn als iframe in Ihre HTML Seite ein.

Für unser Beispiel heißt die wiederverwendbare Klasse in SE24   "/s10/customer". Sie legen in Ihrer ABAP Klasse ein Public-Attribut mit einer Referenz zu dieser Klasse an:

* customer standard functions
     s10customer 
type ref to /s10/customer.

Zum Aufbau des Hierarchiediagramms benötigt das Objekt s10customer die Kundennummer sowie den Vertriebsbereich:

create s10customer object with correct key data
for standard functions (eg customer hierarchy)
    
create object s10customer.
    s10customer
->kunnr kunnr.
    s10customer
->vkorg vkorg.
    s10customer
->vtweg vtweg.
    s10customer
->spart spart.


Die Seite mit der Darstellung der Hierarchie, die Sie einbetten wollen, heißt "customer.hierarchy.html". Sie liegt im SAP MIME Repository in einem anderen Pfad als Ihre Anwendung, sodass Sie eine etwas komplizierte relative URL angeben müssen:

HTML
<div class="subtitle">
   Kundenhierarchie
</div>

<iframe
 
s10object="s10customer"
  src="../../../../../s10/public/classes/customer/views.de/customer.hierarchy.html">
</iframe>

Das heißt, Sie gehen aus Ihrem Projekt in der Pfadhierarchie einige Ebenen nach oben und dann in den "public" Ordner der SE24-Klasse. Dabei ist "s10" der Namensraum aus dem Klassennamen "/s10/customer".

Mehr ist nicht nötig. In den Kundeninformationen erscheint nun die Hierarchiedarstellung:



Die gesamte Funktionalität, also das Lesen der SAP-Kundenhierarchie und daraus die Generierung des Diagramms, ist in der externen Klasse /s10/customer gekapselt und kann in allen Projekten verwendet werden.

Es ist sinnvoll, in der Klassendokumentation zu beschreiben, welche extern verwendbaren Methoden und Sichten die Klasse anbietet.


Anzeigegröße des iframe
Die Größe eines iframes wird normalerweise durch height= und width= gesetzt, optional auch durch die entsprechenden style-Parameter innerhalb von "<iframe>". Als Default nimmt der Browser andernfalls eine Breite von 300px und eine Höhe von 150px an. Eine derartige starre Größe ist in S10 Anwendungen ziemlich unbrauchbar, da sich die Oberfläche an die Displaygrösse des jeweiligen Geräts anpassen soll.

Aus diesem Grund verändert das S10 Framework, falls Sie "height=" nicht angeben, die tatsächlich angezeigte Höhe des iframes dynamisch, sodass sie je nach Breite genau mit der Höhe des Inhaltes übereinstimmt. Als Breite wird dabei die gesamte Breite gesetzt. Im Beispiel oben sieht eine weniger umfangreiche Hierarchie dann so aus:




Sie können auch "width" fest angeben und durch das S10 Framework nur die Höhe anpassen lassen, beispielsweise zwei iframes nebeneinander mit jeweils 50% Breite.

Sonstige Anzeigeattribute des iframe
Sobald Sie in "<iframe" das "s10object=" Attribut angegeben haben, blendet das S10 Framework die Standardumrandung des iframe aus und setzt "margin" und "padding" auf 0, damit der Dialogteil nahtlos in Ihre Seite eingebettet wird. Auch die Scroll-Leisten werden ausgeblendet. Die Hintergrundfarbe wird auf "transparent" gesetzt.

Falls eine Umrandung, Abstand, Scroll-Leisten oder eine Hintergrundfarbe dabei sein sollen, können diese entweder in der eingebetteten Seite oder in Ihrer Seite durch ein übergeordnetes "<div>"-Element gesetzt werden.

In dem Beispiel der Kundenhierarchie ist es sinnvoll, das Diagramm selbst horizontal scrollbar zu machen:


 

Ein vertikales Scrollen des Diagramms ist nicht nötig, da das Scrollen mit der Gesamtseite erfolgt:



Format
Verwenden Sie im iframe-Tag am besten nur die Attribute "s10object=" und "src=", bei Bedarf noch width=:

<iframe
 
s10object="..."
  src="...">
</iframe>

Für den Sonderfall, dass Sie iframes nutzen möchten, die zu dem gerade aktiven Dialogobjekt gehören, bitte die Notation


<iframe
 
s10object="me"
  src="...">
</iframe>

verwenden, denn ohne die s10object= Angabe wird der Einbett-Mechanismus nicht wirksam.


Implementierung des iframe-Objekts
Bei der Implementierung des iframe-Objekts und der einzubettenden HTML-Seite ist nichts besonderes zu beachten. Die HTML-Seite wird wie eine normale HTML-Seite des S10 Frameworks implementiert und kann daher testweise auch separat, also ohne eine Einbettung in eine andere Seite, aufgerufen werden.
Auch ein Aufruf als separates Popup mit s10dialog() ist möglich, ohne die Seite oder die Logik zu verändern.

Bindung der Variablen und Methoden
Innerhalb des iframe beziehen sich alle Variablenbindungen, also der name= Parameter, automatisch auf das mit s10object= angegebene iframe- Objekt. Das heißt,  Eingabefelder transportieren ihren Wert in die Variablen des iframe-Objekts. Validierungs- und build-Methoden laufen ebenfalls dort ab. Methodenaufrufe, z.B. durch Drucktasten ausgelöst, werden in dem iframe-Objekt ausgeführt.


Rückbezug zu dem übergeordneten ABAP-Objekt
In manchen Fällen ist es sinnvoll, eine Benutzeraktion innerhalb des iframe an das übergeordnete Objekt weiterzuleiten, Zum Beispiel könnte das Hierarchiediagramm einen Klick auf einen der dargestellten Kunden erlauben und diesen Klick an das Rahmenprogramm weiterleiten. Das können Sie mit ABAP OO Mitteln abbilden, z.B. indem Sie dem iframe-Objekt noch eine Referenz auf das Rahmenobjekt mitgeben:

s10customer->refobject me.

 und dann eine festgelegte Methode des Rahmenobjekts dynamisch aus dem iframe-Objekt aufrufen.

Eine andere Möglichkeit ist, hier den ABAP-OO-Event-Mechanismus einzusetzen, d.h. das iframe-Objekt löst ein Event aus, für das sich das Rahmenprogramm registiert hat mit der ABAP-Anweisung "Set Handler".

Eine dritte Möglichkeit ist, aus dem iframe heraus auf JavaScript-Ebene eine Funktion des "parent"-Objekts zu rufen, die dann durch S10Apply() eine Methode des Rahmenobjekts aufrufen kann.

Eine vierte Möglichkeit ist, aus dem iframe heraus auf JavaScript-Ebene mit parent.postMessage() eine Nachricht an das Rahmen-Window zu schicken, für die sich das Rahmenwindow mit window.addEventListener registriert hat. Bei Empfang der Nachricht kann dann durch S10Apply() eine Methode des Rahmenobjekts aufgerufen werden. Diese Möglichkeit ist sehr flexibel, da man damit auch lokale Aktionen direkt auf JavaScript-Ebene durchführen kann und die parent.postMessage() Aufrufe nicht stören, wenn das Rahmenwindow keine Behandlung der Nachricht implementiert hat.


ABAP- und HTML-Code des iframe-Objekts im Beispiel

ABAP

 
 method build_hierarchy.

    
clearhierarchyhierarchy_legend.

    
if kunnr is initial or vkorg is initial or vtweg is initial or spart is initial.
      
return.
    
endif.


    
datacustomername type kna1-name1,
          
nodelabel    type string,
          
sales_area   type table of bapi_sdvtber,
          
node_list    type table of bapikna1_knvh,
          
va           type bapi_sdvtber,
          
node         type  bapikna1_knvh,
          
tab          type string,
          
newline      type string.

    
tab cl_abap_char_utilities=>horizontal_tab.
    
newline cl_abap_char_utilities=>newline.

    
va-sales_org vkorg.
    
va-distr_chan vtweg.
    
va-division spart.
    
append va to sales_area.

    
call function 'BAPI_CUSTOMER_GET_ROOT'
      
exporting
        
custhityp  'A'
        customerno 
kunnr
      
tables
        
sales_area sales_area
        node_list  
node_list.

should return one node
    
read table node_list index into node.
    
if sy-subrc ne 0.
      
select single name1 into customername  from kna1 where kunnr kunnr.

      s10writestring
exporting in kunnr attrname 'kunnr' importing out nodelabel ).
      nodelabel
 nodelabel && |<br>| &&  cl_http_utility=>escape_htmlcustomername && || ).
      
hierarchy =  hierarchy && kunnr && tab && nodelabel && tab && | |.
      
return.
    
endif.

    
refresh node_list.
    
call function 'BAPI_CUSTOMER_GET_CHILDREN'
      
exporting
        
custhityp  'A'
        customerno 
node-customer
      
tables
        
sales_area sales_area
        node_list  
node_list.

    
loop at node_list into node.

      
select single name1 into customername  from kna1 where kunnr node-customer.

      s10writestring
exporting in node-customer attrname 'kunnr' importing out nodelabel ).

      nodelabel
 nodelabel && |<br>| &&   cl_http_utility=>escape_htmlcustomername && || ).

      
if node-rebate_rel 'X' or node-pric_rel 'X'.
        
nodelabel =  nodelabel  && |<br>|.
        
if node-rebate_rel 'X'.
          
nodelabel =  nodelabel  && | 💰 |.
        
endif.

        
if node-pric_rel 'X'.
          
nodelabel =  nodelabel  && | 🔖 |.
        
endif.
      
endif.

current customer?
      
if node-customer kunnr.
        
nodelabel |<div style='background-color:#fff9c0;'>| && nodelabel && |</div>|.
      
endif.

      
if  hierarchy is not initial.
        hierarchy 
=  hierarchy && newline.
      
endif.
      
hierarchy =  hierarchy && node-customer && tab && nodelabel && tab && node-parent_customer.

    
endloop.

    
hierarchy_legend |<table><tr><td>💰</td><td>|
                       && 
cl_http_utility=>escape_htmls10localize'bonusrel) )
                       && |</
td></tr><tr><td>🔖</td><td>|
                       &&  
cl_http_utility=>escape_htmls10localize'pricelistrel) )
                       && |</td
></tr></table>|.


  
endmethod.



HTML

<!DOCTYPE html>
<html>
<head>
   
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <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>

    // the chart example is essentially taken from Google API documentation
    // https://developers.google.com/chart/interactive/docs/gallery/orgchart
 

    <
script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
   
<script type="text/javascript">

       google.charts.load('current', { packages: ["orgchart"] });

       
function drawChart(hstring) {

           
var data = new google.visualization.DataTable();
            data.addColumn(
'string', 'Name');
           
data.addColumn('string', 'Manager');


           
var myhierarchy = hstring.split('\n');

            // insert SAP hierarchy data into Google chart data
            for (var k = 0; k < myhierarchy.length; k++) {
                var parts = myhierarchy[k].split('\t');
                data.addRow([{ 'v': parts[0], 'f': parts[1] }, parts[2]]);
            };

           
// create the chart
           
var chart_div = document.getElementById('chart_div');
            var chart = new google.visualization.OrgChart(chart_div);

           
// draw the chart
           
chart.draw(data, { 'allowHtml': true });

           
// left aligned display
           
var tab = chart_div.getElementsByTagName('TABLE')[0];
            if (tab) tab.align = 'left';
        }
   
</script>
</head>
<body>


    <div style="padding:4px; overflow:auto;">

       
<!-- chart area -->
       
<style>
           
.google-visualization-orgchart-space-medium {
               
width: 20px;
            }
       
</style>
        <div id="chart_div"></div>

       
<!-- we add a legend -->
        <div style="clear:both;"></div>
       
<div class="outputhtml" name="hierarchy_legend"></div>

   
</div>

    <!-- hidden field for chart data and start of chart drawing -->
    <input type="hidden" class="output" name="hierarchy" id="hierarchy" onchange="drawChart(this.value);" />
</body>
</html>




Komponente: S10 Framework