Wer mit Oracle APEX arbeitet, kennt das: Ein kleines UX-Detail soll schnell umgesetzt werden – zum Beispiel sollen bei der Eingabe Semikolons durch „Komma + Leerzeichen“ ersetzt werden. Klingt trivial. Ist es auch. Bis plötzlich im Browser steht: Uncaught InternalError: too much recursion
Der Use Case
Stell dir folgendes Szenario vor: Du hast in Oracle APEX ein Formular mit einem Textfeld für die Eingabe von E-Mail-Adressen. Nutzer trennen die Adressen manchmal mit Semikolon, manchmal mit Komma – das Backend erwartet aber immer Komma + Leerzeichen als Trennzeichen.
Die Lösung klingt einfach: Eine Dynamic Action auf das Change-Event des Feldes, die den Wert per JavaScript bereinigt und per setValue() zurückschreibt.
Also legst du in APEX folgende Dynamic Action an:
- Event: Change
- Selection Type: Item
- Item: P1_EMAIL_ADRESSEN
- Action: Execute JavaScript Code
Und der JavaScript Code sieht so aus:
// Erster Versuch - naiv und problematisch var aktuellerWert = $v('P1_EMAIL_ADRESSEN'); var bereinigt = aktuellerWert.replace(/;/g, ', '); $s('P1_EMAIL_ADRESSEN', bereinigt);
Der Fehler: too much recursion
Du speicherst, testest – und der Browser hängt sich auf. In der Konsole steht:
Uncaught InternalError: too much recursion
Was passiert hier? Das Problem ist eine Endlosschleife:
- Nutzer ändert das Feld → Change-Event feuert
- Dynamic Action läuft →
$s()setzt neuen Wert $s()triggert intern wieder ein Change-Event auf dem Feld- Dynamic Action läuft erneut →
$s()setzt Wert… - Und so weiter – bis der Browser aufgibt
$s() in APEX ist ein Wrapper um setValue() und löst bei Items standardmäßig das Change-Event aus – genau das, auf das deine Dynamic Action hört. Ein Teufelskreis.
Lösung 1: Prüfen ob eine Änderung überhaupt nötig ist
Der einfachste Ansatz: Erst prüfen ob der Wert ein Semikolon enthält – und $s() nur dann aufrufen wenn wirklich etwas geändert werden muss:
var aktuellerWert = $v('P1_EMAIL_ADRESSEN');
// Nur ausfuehren wenn Semikolon vorhanden
if (aktuellerWert.indexOf(';') !== -1) {
var bereinigt = aktuellerWert.replace(/;/g, ', ');
$s('P1_EMAIL_ADRESSEN', bereinigt);
}
Das funktioniert in vielen Fällen – aber nicht immer zuverlässig. Wenn der bereinigte Wert sich vom Original unterscheidet, wird $s() aufgerufen, das Change-Event feuert, die Dynamic Action prüft erneut – diesmal kein Semikolon mehr, also kein zweiter Aufruf. Die Rekursion bricht ab. Aber es ist ein fragiles Konstrukt.
Lösung 2: Dynamic Action temporär deaktivieren (Flag-Muster)
Robuster und sauberer ist das Flag-Muster: Du setzt eine globale Variable als Schutz, bevor du $s() aufrufst – und prüfst sie am Anfang der Dynamic Action:
// Flag-Muster gegen Rekursion
if (window._emailBereinigungLaeuft) return;
window._emailBereinigungLaeuft = true;
try {
var aktuellerWert = $v('P1_EMAIL_ADRESSEN');
var bereinigt = aktuellerWert.replace(/;/g, ', ');
if (aktuellerWert !== bereinigt) {
$s('P1_EMAIL_ADRESSEN', bereinigt);
}
} finally {
// Flag immer zuruecksetzen - auch bei Fehler
window._emailBereinigungLaeuft = false;
}
Das try/finally stellt sicher, dass das Flag immer zurückgesetzt wird – auch wenn ein Fehler auftritt. Sauber, sicher, kein Teufelskreis.
Lösung 3: Native DOM-Event statt $s()
Eine weitere Möglichkeit: Den Wert direkt auf das DOM-Element schreiben, ohne APEX-interne Wrapper – und damit das Change-Event gar nicht erst triggern:
var aktuellerWert = $v('P1_EMAIL_ADRESSEN');
var bereinigt = aktuellerWert.replace(/;/g, ', ');
if (aktuellerWert !== bereinigt) {
// Direkt ins DOM schreiben ohne APEX-Event
document.getElementById('P1_EMAIL_ADRESSEN').value = bereinigt;
}
Achtung: Diese Variante umgeht die APEX-interne Verwaltung des Items. Für einfache Textfelder funktioniert das prima – bei Items mit Special Handling (z.B. Datum, LOV) lieber auf Lösung 2 zurückgreifen.
Was du beachten solltest
⚠ $s() triggert immer Change: Das ist kein Bug, sondern Verhalten by design – APEX braucht das für abhängige Dynamic Actions. Rechne immer damit wenn du $s() in einer Change-Dynamic-Action verwendest.
💡 Tipp: Das Flag-Muster funktioniert nicht nur für diesen Use Case. Immer wenn du in APEX JavaScript-Code hast, der selbst Werte ändert auf die er hört, ist das Flag-Muster die sauberste Lösung.
Und noch etwas: Die Browser-Konsole ist dein bester Freund bei solchen Problemen. too much recursion klingt erschreckend – zeigt aber immer auf dasselbe Grundproblem: eine Aktion löst sich selbst aus.
Fazit
Ein setValue() das ein Change-Event auslöst, das wieder setValue() aufruft – klassische Rekursionsfalle in Oracle APEX. Die Lösung ist kein Hexenwerk: entweder prüfen ob eine Änderung wirklich nötig ist, oder das Flag-Muster einsetzen. Einmal verstanden, erkennst du dieses Muster überall – und weißt sofort was zu tun ist wenn der Browser wieder too much recursion meldet.
