Rüdiger
unread,Jun 28, 2010, 10:16:58 AM6/28/10Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to BSP-Praxis
Wie im letzten Posting erwähnt, kann man den Rücktransport von Daten
vom GUI in die HTML-Seite mit der Methode
CL_GUI_HTML_VIEWER=>EXECUTE_SCRIPT erreichen. Diese Methode hat den
Vorteil, dass die Seite nicht komplett neu geladen wird, sondern nur -
ganz gemäss der Ajax-Philosophie - variable Seiteninhalte ausgetauscht
werden.
Der Entwickler der Methode EXECUTE_SCRIPT hatte zwar zarte Vorbehalte
hinsichtlich der Verwendung dieser Methode durch Kreti und Pleti -
weshalb er ihr die Sichtbarkeit "Protected" gab. Hier können wir aber,
Karlsson vom Dach zitierend, antworten: "Das stört doch keinen grossen
Geist". Um die Methode zu verwenden, definieren wir einfach eine
Subklasse LCL_GUI_HTML_VIEWER von CL_GUI_HTML_VIEWER. In dieser können
wir zum Beispiel eine öffentliche Methode definieren, deren Aufrufe an
die Methode EXECUTE_SCRIPT von CL_GUI_HTML_VIEWER delegiert werden.
Und schon ist dieses Hindernis umschifft.
Das Script, das man an den Browser sendet, wird dabei mit ABAP-Mitteln
hergestellt. Es sollte aus einer Reihe von Funktionsaufrufen bestehen,
wobei in den Parametern die dynamischen Daten übergeben werden.
Jegliche Logik sollte bereits in den Funktionen selbst implementiert
sein, so dass der Transport GUI <--> Browser wirklich auf die
"Rohdaten" beschränkt bleibt.
Hier ein Beispiel aus einer produktiv verwendeten Applikation:
...
setField("matnr","675471000000","diff","");
setGlobalWindow(0,0,35,10,1,1);
setSum(1,"50355","9",1,0,0,0,0);
setSum(2,"50356","9",1,0,0,0,0);
setSum(3,"50361","9",1,0,0,0,0);
setSum(4,"50368","9",1,0,0,0,0);
setSum(5,"59050","9",1,0,0,0,0);
setSum(6,"59053","9",1,0,0,0,0);
setSum(7,"59055","9",1,0,0,0,0);
setSum(8,"59056","9",1,0,0,0,0);
g_outer_sums = [21,0,0,0,0,0,0,0]; updSum();headerChanged();
document.screen.fcode.value = "";
doSelections();
setFirstCell();
...
Das Problem ist soweit gut gelöst. Was aber geschieht beim allerersten
Aufruf der Seite? Können wir das Seitentemplate an den Browser senden
und danach mit EXECUTE_SCRIPT die dynamischen Daten hineinstellen?
Das geht leider nicht. Die Scriptausführung kommt zu früh im Browser
an und läuft dann bei der Ausführung auf Fehler. Im GUI-Prozess haben
wir keine Möglichkeit festzustellen, wann das Laden der Seite beendet
ist, was der richtige Zeitpunkt wäre, ab dem das Script fehlerfrei
ausgeführt werden könnte. Was tun?
Meine Lösung besteht darin, statt der BSP-Seite *zunächst einen Loader-
Code in den Viewer zu laden*. Dieser besteht im wesentlichen aus einem
Formular, das zum Ereignis "onload" an die eigentliche BSP-Applikation
gesendet wird und in seinen Post-Daten ein Feld mit dem auszuführenden
JavaScript-Code enthält. Dieser wird dann in der Applikation selbst
mittels der schönen Funktion eval() ausgeführt:
*&---------------------------------------------------------------------
*
*& Form GET_LOADER_CODE
*&---------------------------------------------------------------------
*
* HTML-Code der Zwischenseite beim Laden
* Dient dazu, das initiale Script an die BSP durchzuleiten
*----------------------------------------------------------------------
*
form get_loader_code using iv_bsp_url type string
it_script type tttext255
is_pers_data type zwf50_pers
changing et_code type tttext255.
data: lv_code type string,
lv_line type ttext255.
define _add_line.
concatenate lv_code &1 into lv_code
separated by cl_abap_char_utilities=>cr_lf.
end-of-definition.
_add_line :
'<html>',
' <head>',
' <script type="text/javascript">',
' function jumpToBsp() {',
' document.getElementById("initScript").value =
""+initScript;',
' document.getElementById("jumper").submit()',
' } ',
' function initScript() {'.
loop at it_script into lv_line.
_add_line lv_line.
endloop.
_add_line :
' }',
' </script>',
' </head>',
' <body onload="jumpToBsp()">'.
concatenate lv_code
cl_abap_char_utilities=>cr_lf
' <form style="display:none;" id="jumper"
method="post" action="'
iv_bsp_url
'">'
into lv_code.
_add_line :
' <input type="text" name="initScript" id="initScript"></input>'.
concatenate lv_code
cl_abap_char_utilities=>cr_lf
' <input type="text" name="numRows" value="'
is_pers_data-num_rows
'"></input>'
into lv_code.
concatenate lv_code
cl_abap_char_utilities=>cr_lf
' <input type="text" name="numCols" value="'
is_pers_data-num_cols
'"></input>'
into lv_code.
_add_line :
' </form>',
' </body>',
'</html>'.
clear et_code.
call function 'SCMS_STRING_TO_FTEXT'
exporting
text = lv_code
tables
ftext_tab = et_code.
endform. " GET_LOADER_CODE
Erwähnenswert ist vielleicht noch die Zeile:
document.getElementById("initScript").value = ""+initScript;
Die explizite Type coercion durch Konkatenation der Funktionsreferenz
initScript mit dem leeren String zwingt auch die Funktionsreferenz in
den Typ String (eine implizite Type coercion würde hier auch
funktionieren, man könnte also den Vorsatz ""+ auch weglassen. Aber so
wird die Absicht klarer). Der gesamte Code der Funktion wird somit in
der Form "function () { ... }" in einen String gestellt.
Auf Empfängerseite, also in der BSP, kann man diese Funktion aus dem
Formularparameter entnehmen und schliesslich bei onload ausführen:
In dem für den HTML-Kopfbereich zuständigen BSP-View schreibt man:
<script type="text/javascript">
<%=request->get_form_field('initScript')%>;
</script>
Und bei onload steht dann schliesslich:
<body ... onload="initScript()" ... >
Dies ist der einzige Trick, der mir eingefallen ist, um zu erreichen,
dass beim Ereignis onload einer im HTML Viewer dargestellten Seite
dynamisch generierter JavaScript-Code ausgeführt werden kann. Wer
etwas Besseres weiss, möge sich melden!
Wohlgemerkt, das Problem hat man nur beim initialen Laden der Seite.
Bei allen nachfolgenden Dialogschritten kann JavaScript-Code ganz
geradeaus mit ABAP-Mitteln generiert und mit der Methode
execute_script im HTML Viewer ausgeführt werden.