Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (2024)

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen

Erscheinungsmonat APEX-Version Datenbankversion
Januar 2012 ab 1.5 ab 9.2

Die Unterstützung für tabellarische Formulare ist mit jeder APEX-Version besser und umfassender geworden. APEX 4.1 allein brachte für den Entwickler tabellarischer Formulare eine Menge äußerst nützlicher Dinge mit:

  • Eine explizite Unterstützung für Validierungen - endlich können diese deklarativ festgelegt werden.
  • In eigenen PL/SQL-Prozessen können nun Spaltennamen anstelle der etwas umständlichen APEX_APPLICATION.G_FXX-Arrays verwendet werden.
  • Eigene PL/SQL-Prozesse können deklarativ pro Zeile oder für das ganze Formular ausgeführt werden.
  • Für eigene PL/SQL-Prozesse können Bedingungen pro Zeile festgelegt werden.

Damit können die Assistenten für tabellarische Formulare in immer mehr Situationen genutzt werden - dass man die Formulare wirklich manuell gestalten muss, kommt immer seltener vor. Allerdings bleibt es nicht völlig ausgeschlossen: Es gibt immer noch Situationen, in denen die neuen Möglichkeiten von tabellarischen Formularen nicht genutzt werden können und die manuelle Arbeit erforderlich ist. Einige Gründe sind hier aufgelistet ...

  • Es existiert bereits ein Bericht mit ggfs. komplexerer SQL-Abfrage und einige Spalten sollen zu Eingabespalten werden.
  • Das tabellarische Formular soll auf mehrere Tabellen angelegt werden und das Erstellen einer View im INSTEAD-OF Trigger ist nicht gewünscht.
  • Mit dem tabellarischen Formular sollen gar keine Tabellen gepflegt werden - vielmehr sollen PL/SQL-Prozeduren mit den Formulareingaben gestartet werden.
  • Man bemerkt während dem Erstellen des Formulars mit dem Assistenten, dass "es nicht geht" - der Tabelle fehlt ein Primärschlüssel - man möchte noch zusätzliche Spalten mit SQL-Funktionen hinzufügen und ... und ... und ...

Dieser Tipp widmet sich daher ausführlich dem Thema "manuelle tabellarische Formulare". Allerdings sollten Sie das hier gelesene nur dann einsetzen, wenn Sie Ihre Anforderungen mit den APEX-Standardassistenten wirklich nicht umsetzen können - wenn letztere eine Alternative sind, sind sie die bessere Alternative. Allein die erwähnten neuen Möglichkeiten mit APEX 4.1 (zeilenweile Validierungen, Verwendung von Spaltennamen im PL/SQL-Prozess) sind mit dem hier beschriebenen manuellen Ansatz nicht möglich.

Bericht zur Darstellung des tabellarischen Formulars

Zunächst sei festgehalten, dass ein tabellarisches Formular immer ein Bericht ist. Das Berichts-SQL verwendet das PL/SQL-Paket APEX_ITEM zum Generieren der HTML-Formularelemente in den Berichtszeilen. Und das kann man auch selbst tun, denn APEX_ITEM ist ein dokumentiertes, vom Entwickler nutzbares Paket. Der erste Schritt eines komplett manuellen tabellarischen Formulars ist daher immer der klassische Bericht.

Im folgenden soll ein tabellarisches Formular auf die Tabelle EMP erzeugt werden - erstellen Sie sich dazu eine APEX-Anwendungsseite mit einem klassischen Bericht. Verwenden Sie folgendes Berichts-SQL:

select e.empno, apex_item.hidden( p_idx => 1, p_value => e.empno ) as form_empno, apex_item.md5_hidden( p_idx => 50, p_value01 => e.empno, p_value02 => e.sal, p_value03 => e.deptno ) as form_md5, e.ename, apex_item.text( p_idx => 10, p_value => e.ename, p_size => 15, p_maxlength => 15, p_attributes => 'style="background-color: yellow; border: 1px solid black;"' || 'onBlur="javascript:changeEname('||e.empno||', '''||e.ename||''', this);"' ) as form_ename, apex_item.text( p_idx => 2, p_value => e.sal, p_size => 5, p_maxlength => 5 ) as form_sal, apex_item.select_list_from_query( p_idx => 3, p_value => e.deptno, p_query => 'select dname d, deptno r from dept order by 1', p_show_null => 'NO', p_show_extra => 'NO' ) as form_deptno, d.loc from emp e join dept d on (e.deptno = d.deptno)

Besonders wichtig ist der Parameter p_idx bei den Aufrufen von APEX_ITEM - denn dies ist die Nummer des jeweiligen APEX_APPLICATION.G_FXX-Arrays, unter dem Sie die Formulareingaben später abrufen können. Wichtig für jedes auf diese Art und Weise erzeugtes Formular ist, dass Sie einen eindeutigen Wert pro änderbarer Spalte mit APEX_ITEM.HIDDEN bereitstellen - den brauchen Sie später zum Identifizieren der Formularspalten. Die jeweiligen Prozeduren in APEX_ITEM erzeugen unterschiedliche Formularelemente - schauen Sie sich den generierten Bericht einfach mal an (Abbildung1).

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (1)

Abbildung 1: Der mit "APEX_ITEM" erzeugte Bericht in einer ersten Ansicht

Das hat noch nicht viel von einem tabellarischen Formular: Es ist bis jetzt ein einfacher, klassischer Bericht - und der generierte HTML-Code wird dank des standardmäßig aktiven Schutzes vor Cross-Site-Scripting (XSS) "escaped" dargestellt. Zunächst stellen Sie also die Darstellung aller Berichtsspalten, die mit APEX_ITEM erzeugt wurden, auf Standardberichtsspalte um - dazu navigieren Sie zu den Berichtsattributen und dann zu den jeweiligen Spaltenattributen (Abbildung 2). Entfernen Sie dann noch die Überschriften der "Hidden"-Spalten FORM_MD5 und FORM_EMPNO - entfernen Sie aber nicht das Häkchen bei Spalte anzeigen.

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (2)

Abbildung 2: Spaltendarstellung auf "Standardberichtsspalte" umstellen

Nun sieht die Seite schon eher nach einem tabellarischen Formular aus (Abbildung 3).

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (3)

Abbildung 3: Das tabellarische Formular wird nun richtig dargestellt

Nun geht es daran, die Formulareingaben zu verarbeiten. In diesem Tipp werden zwei Varianten vorgestellt. Zunächst soll das gelb unterlegte Feld für den ENAME eine "Sofortänderung" ermöglichen: Sobald man hier etwas ändert und es verlässt, soll der neue Wert ohne Umweg in die Tabelle geschrieben werden. Diese Technik kann zur schnellen Änderung von einzelnen Feldern (bspw. "Kommentare") durchaus interessant sein.

"Sofortänderung" eines Feldes mit AJAX und JavaScript

Beim Aufruf von APEX_ITEM wurden für das gelb unterlegte Feld im Parameter P_ATTRIBUTES zwei HTML-Attribute mitgegeben: Mit dem Attribut style wurde es gelb unterlegt - und onBlur sorgt dafür, dass beim Verlassen des Feldes die JavaScript-Funktion changeEname aufgerufen wird - mit der EMPNO, dem "alten" ENAME und dem Eingabefeld selbst als Parameter. Diese JavaScript-Funktion wird nun implementiert:

function changeEname(pEmpno, pOldEname, pInput) { if (pInput.value != pOldEname) { var get = new htmldb_Get(null,&APP_ID.,'APPLICATION_PROCESS=changeEname',0); get.addParam('x01', pOldEname); get.addParam('x02', pInput.value); get.addParam('x03', pEmpno); gReturn = get.get(); if (gReturn != 'SUCCESS') { alert(gReturn); } }}

Navigieren Sie zu den Seitenattributen, dort zu JavaScript und tragen Sie den Code dort ein. Bei älteren APEX-Versionen tragen Sie ihn in den Seiten Header ein; denken Sie dann aber daran, ihn zwischen die HTML-Tags <script type="text/javascript"> und </script> zu setzen.

Die Funktion wird stets beim Verlassen des Eingabefeldes aufgerufen. Zunächst wird verglichen, ob der Inhalt überhaupt verändert wurde - dazu war es wichtig, den alten Wert von ENAME mit der SQL-Abfrage direkt in den JavaScript-Aufruf hineinzugenerieren. Wenn eine Änderung stattfand, wird mit htmldb_Get ein AJAX-Request ausgeführt und der Anwendungsprozess changeEname aufgerufen. Die EMPNO, der alte ENAME und der neue ENAME werden als Paramater x01, x02 und x03 übergeben. Generell können bis zu 10 "x"-Parameter übergeben werden - Anwendungselemente sind hierfür also nicht nötig. Der Anwendungsprozess wird bei den Gemeinsamen Komponenten unter Anwendungsprozesse als Bedarfsgesteuerter Prozess eingerichtet (Abbildung 4).

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (4)

Abbildung 4: Erstellen eines Bedarfsgesteuerten Anwendungsprozesses

Hinterlegen Sie folgenden PL/SQL Code:

declare l_oldename varchar2(100) := apex_application.g_x01; l_newename varchar2(100) := apex_application.g_x02; l_empno varchar2(100) := apex_application.g_x03; l_message varchar2(100) := ''; l_proceed boolean := true;begin -- Prüfe auf "lost update" begin select empno into l_empno from emp where empno = l_empno and ename = l_oldename for update; exception when NO_DATA_FOUND then l_message := 'Diese Zeile hat sich zwischenzeitlich geändert - bitte laden Sie das Formular neu'; l_proceed := false; when TOO_MANY_ROWS then l_message := 'Unerwarteter Fehler - bitte kontaktieren Sie den Helpdesk'; l_proceed := false; end; if l_proceed then update emp set ename = l_newename where empno = l_empno; l_message := 'SUCCESS'; end if; htp.prn(l_message);end;

Probieren Sie es aus - man sieht auf den ersten Blick nicht viel, wenn man die gelb unterlegten Felder ändert und verlässt. Im Hintergrund tut sich aber schon etwas - wie Abbildung 5 mit einer Ausgabe der Firebug-Konsole zeigt.

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (5)

Abbildung 5: Hintergrundaktivität durch den AJAX-Request

Diese Vorgehensweise eignet sich gut, um einzelne Spalten eines APEX-Berichts für den Endanwender einfach und ohne viel Aufwand änderbar zu machen. "Paradebeispiele" sind Kommentarfelder, die der Benutzer bei Durchsicht einer Datenreihe schnell pflegen möchte. Aber auch Auswahllisten für Statusfelder können so einfach implementiert werden. Es wäre auch möglich, alle Felder auf diese Art und Weise änderbar zu machen; der Code für die JavaScript-Funktion und den Anwendungsprozess müsste dazu aber generischer sein.

Noch etwas AJAX: Spalte LOC dynamisch aktualisieren

An dieser Stelle bietet es sich an, das Formular um noch etwas AJAX zu erweitern: Wenn die Auswahlliste für die DEPTNO geändert wird, soll der Ort in der Spalte LOC ebenfalls aktualisiert werden (gespeichert wird noch nichts, das geschieht im nächsten Absatz dieses Tipps). Da wir alle Elemente des tabellarischen Formulars selbst kontrollieren, ist das kein Problem: Zuerst muss das Berichts-SQL angepasst werden. Der Aufruf von APEX_HIDDEN.SELECT_LIST_FROM_QUERY muss um den Parameter P_ATTRIBUTES erweitert werden.

 : apex_item.select_list_from_query( p_idx => 3, p_value => e.deptno, p_query => 'select dname d, deptno r from dept order by 1', p_attributes => 'onChange="javascript:getLoc('||e.empno||', this);"', p_show_null => 'NO', p_show_extra => 'NO' ) as form_deptno, :

Nun wird bei jeder Änderung (onChange) die JavaScript-Funktion getLoc aufgerufen. Diese ruft nun per AJAX-Request den Ort des Departments (DEPT.LOC) ab und trägt ihn in die Spalte rechts neben der Auswahlliste ein. Damit das von JavaScript aus möglich wird, muss das jeweilige Feld identifizierbar gemacht werden. Das geschieht, indem dort ein HTML-Tag mit einem ID-Attribut eingetragen wird. Navigieren Sie also zu den Berichtsattributen, dort zur Spalte LOC und dann zum Bereich HTML-Ausdruck. Tragen Sie dort, wie in Abbildung 6 ersichtlich, folgendes ein: <spanid="LOC_#EMPNO#">#LOC#</span>.

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (6)

Abbildung 6: Zeilen in der Berichtsspalte LOC identifizierbar machen

Nun braucht es die JavaScript-Funktion getLoc. Navigieren Sie, wie vorhin, wieder zu den Seitenattributen, dort zum Bereich JavaScript (wo schon die erste JavaScript-Funktion vorhanden ist), und setzen Sie diesen Code ein:

function getLoc(pEmpno, pInput) { var get = new htmldb_Get(null,&APP_ID.,'APPLICATION_PROCESS=getLoc',0); get.addParam('x01', pInput.value); gReturn = get.get(); $("#LOC_"+pEmpno).text(gReturn);}

Fehlt zum Abschluß noch der Anwendungsprozess zum Auslesen der LOC aus einer Tabelle. Navigieren Sie zu den Gemeinsamen Komponenten, dort zu den Anwendungsprozessen und erzeugen Sie wiederum einen Bedarfsgesteuerten Prozess namens getLoc mit folgendem PL/SQL Code:

declare l_deptno varchar2(100) := apex_application.g_x01; l_loc dept.loc%type;begin begin select loc into l_loc from dept where deptno = l_deptno; exception when NO_DATA_FOUND then l_loc := '-UNKNOWN-'; when TOO_MANY_ROWS then l_loc := '-UNKNOWN-'; end; htp.prn(l_loc);end;

Und das ist es! Wenn Sie die Seite nun starten und die Auswahlliste für die DEPTNO ändern, ändert sich auch der Inhalt der Spalte LOC daneben. AJAX kann also durchaus auch in einem tabellarischen Formular genutzt werden.

Klassische Verarbeitung des tabellarischen Formulars

Nachdem die "Instant"-Änderung eines Formularfeldes mit AJAX funktioniert, soll als nächstes die "klassische" Verarbeitung des Formulars mit einem selbstgeschriebenen onSubmit-Prozess implementiert werden. Dazu wird zunächst eine Schaltfläche zum Absenden der Seite benötigt. Fügen Sie diese hinzu.

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (7)

Abbildung 7: Eine Schaltfläche zum Absenden des Formulars wurde hinzugefügt

Die Verarbeitung erfolgt nun in einem "normalen" Seitenprozess, der Beim Weiterleiten (onSubmit) der Seite ausgeführt wird. Im PL/SQL-Code können Sie auf die Formularinhalte über die PL/SQL-Arrays APEX_APPLICATION.G_FXX zugreifen, wobei XX für die Zahl steht, die Sie bei der Berichtsabfrage als Parameter P_IDX vergeben haben. Beachten Sie aber, dass die Arrays nur für Prozesse der Seitenverarbeitung (Page Submit) zur Verfügung stehen; während der onLoad-Phase sind sie immer leer. In diesem Beispiel enthält das Array ...

  • G_F01 die nicht dargestellte EMPNO-Spalte
  • G_F02 die Spalte mit dem Gehalt
  • G_F03 die Spalte mit den Auswahllisten für die DEPTNO
  • G_F10 die Spalte mit den gelb unterlegten Eingabefeldern für ENAME
  • G_F50 die nicht dargestellte Spalte mit MD5-Checksummen für das optimistische Locking

Mit diesem Wissen kann der PL/SQL-Prozess erstellt werden. Knüpfen Sie ihn an die Bedingung, dass er nur ausgeführt wird, wenn die soeben erstellte Schaltfläche geklickt wurde und hinterlegen Sie folgenden PL/SQL Code ...

declare la_empno apex_application_global.vc_arr2 := apex_application.g_f01; la_sal apex_application_global.vc_arr2 := apex_application.g_f02; la_deptno apex_application_global.vc_arr2 := apex_application.g_f03; la_ename apex_application_global.vc_arr2 := apex_application.g_f10; la_md5 apex_application_global.vc_arr2 := apex_application.g_f50; l_md5 varchar2(100); l_deptno emp.deptno%type; l_sal emp.sal%type;begin apex_collection.create_or_truncate_collection('TABFORM_ERRORS'); for r in la_empno.first..la_empno.last loop select wwv_flow_item.md5(empno, sal, deptno), sal, deptno into l_md5, l_sal, l_deptno from emp where empno = la_empno(r) for update; if l_md5 != la_md5(r) then apex_collection.add_member( p_collection_name => 'TABFORM_ERRORS', p_c001 => la_empno(r), p_c002 => 'Row changed in the meantime', p_c003 => la_sal(r), p_c004 => l_sal, p_c005 => la_deptno(r), p_c006 => l_deptno ); else begin update emp set sal = la_sal(r), deptno = la_deptno(r) where empno = la_empno(r); exception when others then apex_collection.add_member( p_collection_name => 'TABFORM_ERRORS', p_c001 => la_empno(r), p_c002 => sqlerrm, p_c003 => la_sal(r), p_c004 => l_sal, p_c005 => la_deptno(r), p_c006 => l_deptno ); end; end if; end loop;end;

Aufmerksame Leser haben wahrscheinlich schon festgestellt, dass sich der Prozess per Schleife durch das Array G_F01 (das ist die mit APEX_HIDDEN erzeugte Spalte mit den EMPNO-Werten) durcharbeitet.

  • Zunächst wird die MD5-Checksumme anhand der nun aktuellen Daten in der Tabelle ermittelt. Gleichzeitig werden die Tabellendaten in lokale PL/SQL Variablen eingelesen.
  • Danach wird die Checksumme mit der im SQL-Bericht ebenfalls ermittelten Checksumme, die im Array G_F50 enthalten ist, verglichen.
  • Wenn die Checksummen unterschiedlich sind, wurde die Tabellenzeile im Hintergrund geändert (optimistisches Locking). Im Gegensatz zu einem Standard-Tabellenformular lösen wir nun aber keine Fehlermeldung aus, sondern speichern den Satz in der APEX-Collection TABFORM_ERRORS ab.
  • Danach findet das eigentliche Update statt. Wenn dabei ein Fehler auftritt (bspw. verletzte Constraints), wird dieser abgefangen und besagte Zeile ebenfalls in die Collection abgelegt.

Als Ergebnis haben wir ein tabellarisches Formular mit optimistischem Locking, genauso wie das APEX-Standardformular. Allerdings wird bei auftretenden Fehlern, keine eigene Fehlermeldung ausgelöst. Vielmehr werden die fraglichen Sätze in eine Collection geschrieben, die man natürlich auch per Bericht anzeigen kann. Das Berichts-SQL ist recht einfach - denn aus der View APEX_COLLECTIONS können die Inhalte der APEX-Collection selektiert werden. Abbildung 8 zeigt, wie das Nutzerfeedback aussehen könnte.

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (8)

Abbildung 8: Ein Bericht auf die Collection TABFORM_ERRORS zeigt die Datensätze mit Fehlern an

Und da eine APEX Collection verwendet wurde, muss man sich um das "Aufräumen" nicht weiter kümmern, das macht APEX von alleine. Der PL/SQL Prozess zum Verarbeiten des Formulars kann natürlich beliebig programmiert werden - neben dem einfachen Update der Tabelle EMP sind natürlich noch weitere DML-Operationen, der Aufruf von Stored Procedures und andere Dinge möglich. Dem Programmierer sind keine Grenzen gesetzt. Auf dem gleichen Weg sind übrigens auch mehrere tabellarische Formulare auf einer Seite möglich. Eine Begrenzung stellt lediglich die Anzahl der verfügbaren PL/SQL-Arrays dar - APEX erlaubt maximal 50. Und da jede mit APEX_ITEM erzeugte Spalte, in jedem Formular, eine eigene Nummer braucht, darf es also in allen tabellarischen Formularen einer Seite zusammen nur 50 davon geben.

Fazit

Tabellarische Formulare in APEX können sehr vielfältig sein - neben den APEX-Assistenten zum Erzeugen standardisierter Formulare kann man immer noch (wie damals mit HTML DB 1.5) komplett manuell arbeiten. Mit "manuellen tabellarischen Formularen" kann man viele Beschränkungen umgehen und sogar Eingabefelder unmittelbar (ohne Weiterleiten der Seite) abspeichern, sie erfordern jedoch mehr Aufwand bei der Entwicklung und sind schwerer wartbar - da die Details sich im SQL- und PL/SQL Code verbergen. Standardformulare auf der anderen Seite sind zwar schnell erstellt und leicht wartbar, da deklarativ, bringen jedoch Einschränkungen mit sich.

In der Praxis empfiehlt es sich, abzuwägen: Können große Teile oder gar die ganze Anforderung mit Standardmitteln abdeckt werden, so sollte man diese nehmen - bleiben Lücken oder können sogar wesentliche Teile der Anforderungen nicht abgedeckt werden, so ist es gut zu wissen, dass man das Problem auf dem manuellen Weg auf jeden Fall lösen kann.

Zurück zur Community-Seite

Tipps und Tricks mit manuell erzeugten tabellarischem Formularen (2024)
Top Articles
Latest Posts
Article information

Author: Eusebia Nader

Last Updated:

Views: 6436

Rating: 5 / 5 (60 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Eusebia Nader

Birthday: 1994-11-11

Address: Apt. 721 977 Ebert Meadows, Jereville, GA 73618-6603

Phone: +2316203969400

Job: International Farming Consultant

Hobby: Reading, Photography, Shooting, Singing, Magic, Kayaking, Mushroom hunting

Introduction: My name is Eusebia Nader, I am a encouraging, brainy, lively, nice, famous, healthy, clever person who loves writing and wants to share my knowledge and understanding with you.