^ AJAX Programmierung in APEX-Anwendungen: Wer nutzt noch "htmldb_Get"?
Logo Oracle Deutschland   Application Express Community
AJAX Programmierung in APEX-Anwendungen: Wer nutzt noch htmldb_Get?
Erscheinungsmonat APEX-Version Datenbankversion
März 2017 ab 5.0 ab 11.1

Der Umgang mit AJAX-Technologie, also dem Absetzen einer separaten HTTP-Abfrage und dem dynamischen Verändern der APEX-Seite, ist mittlerweile vielen vertraut. Allerdings zeigen sich in der Praxis recht unterschiedliche Wege, AJAX umzusetzen. Vor allem in älteren Anwendungen findet sich vielfach noch die Javascript-Funktion htmldb_Get.

Im jüngsten Application Express Release 5.1 wurde htmldb_Get desupported, das ist in den Release Notes ersichtlich. Aber eigentlich war es noch die wirklich dokumentiert; die Application Express API Reference enthielt noch nie einen Eintrag zu htmldb_Get.

Es ist also an der Zeit, sich von htmldb_Get zu verabschieden - das bedeutet in erster Linie natürlich, dass man es nicht mehr erneut einsetzt. Wenn sich darüber hinaus eine gute Gelegenheit ergibt, sollten bestehende Vorkommen ersetzt werden. Daher stellt dieser Community-Tipp eine gängige AJAX-Problemstellung (Abbildung 1) und aktuelle Lösungsansätze dazu vor.

Die Aufgabe: Im Textbereich sollen Informationen zum ausgewählten Eintrag erscheinen

Abbildung 1: Die Aufgabe: Im Textbereich sollen Informationen zum ausgewählten Eintrag erscheinen

Die Auswahlliste (P1_EMPNO) wird mit den Spalten EMPNO und ENAME der Tabelle EMP aufgebaut - sobald einer ausgewählt wurde, sollten, und zwar ohne dass die Seite neu geladen wird, Informationen zur Auswahl im Textbereich (P1_EMP_INFO) angezeigt werden: das ist eine ganz klassische AJAX-Aufgabe, mit denen die Alternativen zu htmldb_Get sehr gut aufgezeigt werden können.

1. Dynamic Actions

Das Nutzen von Dynamic Actions ist die mit Sicherheit einfachste und in den meisten Fällen auch die beste Lösungsvariante. Die gesamte AJAX-Aufgabe wird deklarativ in APEX hinterlegt; APEX kümmert sich selbst um den nötigen JavaScript-Code. Die nötigen Dynamic Actions werden wie folgt erzeugt.

  • Erstellen Sie im Page Designer eine neue Dynamic Action für das Element P1_EMPNO (die Auswahlliste).
    Eine neue Dynamic Action erstellen

    Abbildung 2: Eine neue Dynamic Action erstellen

  • Im Property Editor auf der rechten Seite vergeben Sie zuerst einen Namen und legen Sie dann fest, dass die dynamische Aktion dann ausgeführt werden soll, wenn die Auswahlliste P1_EMPNO verändert (Change-Event) wird - der neue Wert darf aber nicht NULL sein (Abbildung 3).
    Details, wann die Dynamic Action ausgelöst werden soll

    Abbildung 3: Details, wann die Dynamic Action ausgelöst werden soll

  • Dann legen Sie fest, was genau passieren soll. Klicken Sie im Strukturbaum links die (noch rot markierte) TRUE Action an und ändern Sie deren Eigenschaften im Property Editor rechts: Nehmen Sie setValue als Action und SQL Statement als Set Type. Wählen Sie dann das Element P1_EMP_INFO als Affected Element aus.
    Aktion "Set Value": Details

    Abbildung 4: Aktion "Set Value": Details

  • Hinterlegen Sie dann die SQL-Abfrage, mit deren Ergebnis das Element befüllt werden soll. Sie sollte genau eine Spalte und eine Zeile zurückliefern. Wichtig ist auch, dass Sie das Element P1_EMPNO bei Page Items To Submit eintragen, damit der in der Auswahlliste selektierte Eintrag auch wirklich bei der SQL-Abfrage ankommt.
    SQL Abfrage für die Dynamic Action

    Abbildung 5: SQL Abfrage für die Dynamic Action

Wenn Sie die Seite nun testen, bemerken Sie, dass der Inhalt des Textbereichs sich ändert, sowie Sie in der Auswahlliste einen neuen Eintrag wählen. Sie haben Ihre APEX-Seite mit AJAX-Funktionalität versehen, ohne auch nur eine Zeile Javascript-Code zu schreiben. Und da die dynamischen Aktionen ein Teil der APEX-Metadaten sind, stehen alle Informationen dazu in den APEX Dictionary Views zur Verfügung.

Das Ergebnis: Nach Auswahl eines Eintrags erscheinen Detailinformationen in der Textbox

Das Ergebnis: Nach Auswahl eines Eintrags erscheinen Detailinformationen in der Textbox

Besser geht es eigentlich nicht - und Dynamic Actions können noch mehr, als APEX-Elemente zu setzen. Man sollte eigenen Javascript-Code, also die nun folgende Variante 2, nur dann einsetzen, wenn die Anforderung mit Dynamic Actions nicht umgesetzt werden kann. Sie werden feststellen, dass dies nur sehr selten der Fall ist.

2. Manuelle AJAX-Implementierung

Im Vergleich zur bereits besprochenen Variante mit Dynamic Actions ist die folgende, "manuelle", Vorgehensweise wesentlich aufwändiger. Bevor Sie loslegen, löschen Sie jedoch die soeben erstellte Dynamic Action oder sorgen Sie per Bedingung (Condition) dafür, dass sie Nie (Never) ausgeführt wird. Alternativ kann man dafür auch die Erstelloptionen (Build Options) nutzen.

Zuerst wird die serverseitige Komponente, also der Teil, der die SQL-Abfrage ausführt, erstellt. Hier haben Sie zwei Möglichkeiten: Wenn Ihr AJAX-Prozess vor allem im Kontext einer APEX-Seite Sinn macht, dann legen Sie den serverseitigen Teil als AJAX-Callback auf der Seite selbst an. Wenn Sie den serverseitigen Teil dagegen anwendungsweit verwenden möchten, dann (und nur dann) nehmen Sie einen Anwendungsprozess in den Gemeinsamen Komponenten. Wenn Sie, im weiter unten folgenden Javascript-Teil, einen Prozess ansprechen, der sowohl als gemeinsam verwendbarer Anwendungsprozess als auch als seitenspezifischer AJAX Callback existiert, so verwendet APEX den AJAX-Callback.

  • Wenn Sie einen Anwendungsprozess verwenden möchten, gehen Sie wie folgt vor:

    Beginnen Sie in den Gemeinsamen Komponenten mit dem Erstellen eines Anwendungsprozesses. Erzeugen Sie ihn als On-Demand-Prozess und nennen Sie ihn getEmpInfo (Abbildung 6).

    Neuen On-Demand-Anwendungsprozess erstellen

    Abbildung 7: Neuen On-Demand-Anwendungsprozess erstellen

    Hinterlegen Sie den folgenden PL/SQL-Code und speichern Sie den Anwendungsprozess danach ab.

    declare
      l_emprow emp%ROWTYPE;
    begin
      select * into l_emprow
      from emp 
      where empno = apex_application.g_x01;
      htp.p('EMPNO: '||l_emprow.empno||chr(10)||
            'ENAME: '||l_emprow.ename||chr(10)||
            'SAL:   '||l_emprow.sal  ||chr(10)||
            'JOB:   '||l_emprow.job);
    end;
    

    Wenn Sie per AJAX ein INSERT, UPDATE oder DELETE ausführen, sollten Sie mit HTP.P zumindest ein SUCCESS oder eine andere Statusmeldung zurückgeben, damit Sie im (folgenden) Javascript-Teil den Status Ihrer Operation prüfen können.

    Achten Sie darauf, dass die Autorisierung des Applicaton Process richtig gesetzt ist. Soll er aus öffentlichen Seiten (also ohne Anmeldung) heraus verwendet werden, so muss das Attribut Autorisierung (Authorization) leer sein (Must Not Be Public User darf nicht gesetzt sein). Umgekehrt sollte es gesetzt sein, wenn der Anwendungsprozess nur ausgeführt werden darf, wenn ein User angemeldet ist.

    Autorisierung des Anwendungsprozesses prüfen - nur bei "öffentlichen" Anwendungen

    Abbildung 8: Autorisierung des Anwendungsprozesses prüfen - nur bei "öffentlichen" Anwendungen

  • Wenn Sie mit einem (seitenspezifischen) AJAX Callback arbeiten, gehen Sie so vor:

    Klicken Sie im Page Designer auf der linken Seite den Reiter für das Processing - wo die Seitenprozesse verwaltet werden. Im Eintrag AJAX Callbacks öffnen Sie das Kontextmenü und klicken Create Process. Daraufhin erscheint auf der rechten Seite ein neuer AJAX Callback mit einigen Defaults.
    Neuen AJAX Callback erstellen

    Abbildung 9: Neuen AJAX Callback erstellen

    Hinterlegen Sie den oben dargestellten PL/SQL Code. Nennen Sie Ihren AJAX-Callback getEmpInfo.

    Namen und PL/SQL Code für den AJAX Callback hinterlegen

    Abbildung 10: Namen und PL/SQL Code für den AJAX Callback hinterlegen

    Auch hier gilt, dass Sie Ihr PL/SQL Code in jedem Fall mit HTP.P eine Ausgabe produzieren sollte - wenn Sie per AJAX ein INSERT, UPDATE oder DELETE ausführen, sollten Sie zumindest ein SUCCESS zurückgeben, damit Sie im (folgenden) Javascript-Teil den Status Ihrer Operation prüfen können.

Damit haben Sie die erste Hälfte der "manuellen" AJAX Implementierung, den serverseitigen Prozess, fertiggestellt. Nun geht es an den browserseitigen Javascript-Teil: Navigieren Sie zu den Seitenattributen der Anwendungsseite, und dort zum Bereich Javascript. Tragen Sie dann bei Deklaration der Funktion und globalen Variable folgenden Javascript-Code ein.

function getEmpInfo () {
  apex.server.process(
    'getEmpInfo',                             // Process or AJAX Callback name
    {x01: apex.item("P1_EMPNO").getValue()},  // Parameter "x01"
    {
      success: function (pData) {             // Success Javascript
        apex.item("P1_EMP_INFO").setValue(pData);
      },
      dataType: "text"                        // Response type (here: plain text)
    }
  );
}

Die JavaScript-Funktion apex.server.process führt einen AJAX-Request zum APEX-Server durch. Dies ist die "offizielle", dokumentierte Javascript-Funktion für AJAX-Requests, die man anstelle von htmldb_Get verwenden sollte. Die Dokumentation zu apex.server.process finden Sie bei den Application Express Javascript APIs.

Als ersten Parameter übergeben Sie den Namen des Application-Prozesses. Danach kommen übergebene Parameter und APEX-Elemente. Im Gegensatz zur Dynamic Action-Variante wird das APEX-Element P1_EMPNO hier nicht an den APEX-Server gesendet - dessen Inhalt wird allerdings als AJAX-Parameter x01 übergeben. Im PL/SQL-Code des Anwendungsprozesses wird der Parameter mit APEX_APPLICATION.G_X01 angesprochen. Sie können die Parameter x01 bis x10 verwenden - damit steht Ihnen neben dem Submit eines APEX-Elements noch eine weitere Variante zur Parameterübergabe an den Server zur Verfügung.

Für die Experten sei noch gesagt, dass das Absenden eines APEX-Elements sich (natürlich) auf den APEX Session State auswirkt und damit auch zu COMMITs in der Datenbank führt. Die Parameter x01 bis x10 sind dagegen einfache PL/SQL-Parameter - wenn Sie also auf dem Server eine große Last durch AJAX-Prozesse erwarten, ist die Nutzung der "x-Parameter" ressourcenschonender als ein "Submit" der APEX-Elemente.

Der Bereich success im JavaScript-Code enthält wiederum eine JavaScript-Funktion; diese wird, wie der Name schon nahelegt, im Erfolgsfall ausgeführt. Im Beispiel wird einfach das APEX-Element P1_EMP_INFO mit der Server-Antwort des AJAX-Aufrufs gesetzt (Javascript-Funktion apex.item.setValue). Analog dazu könnte man mit error auch Javascript-Code für den Fehlerfall hinterlegen. Der Parameter dataType schließlich legt fest, welche Antwort vom Server erwartet wird - in unserem Fall ist das einfacher Text, daher wird auch text angegeben. Wenn Sie hier nichts angeben, erwartet apex.server.process, dass die Serverseite mit JSON-Syntax antwortet.

Der letzte Schritt ist nun, die gerade erzeugte Javascript-Funktion getEmpInfo() dann aufzurufen, wenn der Endanwender die Auswahlliste P1_EMPNO ändert. Zum Setzen des Event-Handlers können Sie nun, obwohl Sie die gesamte Implementierung bereits mit JavaScript gemacht haben, wieder eine Dynamic Action einsetzen. Diese wird dann wie oben deklariert, als Aktion nehmen Sie dann aber JavaScript-Code ausführen (Abbildung 11).

JavaScript-Code per Dynamic Action ausführen

Abbildung 11: JavaScript-Code per Dynamic Action ausführen

Alternativ können Sie Event-Handler jedoch auch "manuell", allein mit JavaScript Code, setzen und komplett auf Dynamic Actions verzichten. Tragen Sie dazu (in den Seitenattributen) unter Beim Seitenladen ausführen folgende Javascript-Zeile ein.

$("#P1_EMPNO").on("change", getEmpInfo);

Starten Sie die Seite und probieren Sie es aus. Sie sollte nun genau so reagieren, wie vorher mit den Dynamic Actions. Der Unterschied ist, dass Sie die Implementierung nun manuell gemacht haben. Wie immer ist eine manuelle Implementierung aufwändiger und in den APEX-Metadaten auch nicht so gut nachvollziehbar wie das Standard-Vorgehen mit Dynamic Actions. Dafür bekommt man viele Möglichkeiten, das Verhalten im Detail zu beeinflussen.

Das Ergebnis: Nach Auswahl eines Eintrags erscheinen Detailinformationen in der Textbox

Das Ergebnis: Nach Auswahl eines Eintrags erscheinen Detailinformationen in der Textbox

Fazit

Wie schon erwähnt, sind Dynamic Actions das Mittel der Wahl zur Umsetzung von AJAX-Funktionalität in einer APEX-Anwendung. Komplexere Fälle oder solche, bei denen das Verhalten im Detail beeinflusst werden soll, lassen sich mit manuellem JavaScript-Code und der Funktion apex.server.process umsetzen. Sinnvoll kann das bspw. sein, wenn Sie sehr viele Dynamic Actions auf Ihrer APEX-Seite brauchen: Die Erfahrung hat gezeigt, dass Anwendungsseiten nur noch schwer wartbar sind, wenn die Anzahl der Dynamic Actions überhand nimmt.

Wie Sie Dynamic Actions und eigenen JavaScript-Code miteinander integrieren können, wurde bereits in einem Community-Tipp beschrieben. Schauen Sie einfach nochmal rein.

Zurück zur Community-Seite