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:

  1. Nutzer ändert das Feld → Change-Event feuert
  2. Dynamic Action läuft → $s() setzt neuen Wert
  3. $s() triggert intern wieder ein Change-Event auf dem Feld
  4. Dynamic Action läuft erneut → $s() setzt Wert…
  5. 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.