Ordenando tu JavaScript en Oracle APEX con el Patrón Namespace Parte 1
Cuando tu app en APEX crece, el JavaScript suele crecer “a la mala”: funciones sueltas en el Global Scope, nombres duplicados, y lógica de UI regada en 10 Dynamic Actions diferentes. El resultado: mantenimiento caro, bugs difíciles de rastrear y cero consistencia.
El patrón Namespace es una solución simple y efectiva: en lugar de llenar window con funciones sueltas, agrupas tu código en un objeto raíz (por ejemplo App) y lo organizas por módulos (App.apex, App.rules, etc.). Eso te da orden, escalabilidad y un sitio único donde encontrar las cosas.
El problema: JavaScript suelto por todas partes
Esto es típico:
- En una DA pones
hideTotal() - En otra DA defines
setStatus() - En otra DA agregas un
toggleFields()… y a los 3 meses ya nadie sabe dónde vive cada cosa
Además, si alguien crea otra función con el mismo nombre en otra página, buena suerte.
La idea del Namespace
Un namespace es un “contenedor” que cuelga de un objeto raíz (normalmente window). Ejemplo mental:
App.apex→ utilidades genéricas para APEXApp.rules→ reglas de pantalla y lógica de UI (qué mostrar/ocultar según valores)- En el futuro podrías tener:
App.ajax,App.ui,App.security, etc.
Paso 1: una función mínima para crear/obtener namespaces
Esta función te permite escribir ns("App.apex") y asegurar que exista window.App.apex.
function ns(path) {
const parts = String(path).split('.').filter(Boolean);
let ctx = window;
for (const p of parts) {
ctx[p] = ctx[p] || {};
ctx = ctx[p];
}
return ctx;
}
Qué hace:
- Divide el path por puntos
- Crea objetos intermedios si no existen
- Devuelve el objeto final
Con esto, dejas de escribir if (!App) App = {}; if (!App.apex) App.apex = {}; manualmente.
Paso 2: Un módulo App.apex con wrappers básicos
Aquí viene el “hack” práctico: si siempre estás escribiendo apex.item("P1_X").hide() por toda la app, centralízalo.
ns("App.apex");
App.apex.show = function(itemName) {
apex.item(itemName).show();
};
App.apex.hide = function(itemName) {
apex.item(itemName).hide();
};
App.apex.setValue = function(itemName, value) {
apex.item(itemName).setValue(value);
};
App.apex.getValue = function(itemName) {
return apex.item(itemName).getValue();
};
Beneficios inmediatos:
- Menos repetición
- Nombres consistentes
- Si mañana quieres meter validaciones (por ejemplo, si el item no existe), lo haces en un solo lugar
Paso 3: Uso desde cualquier Dynamic Action (o Console)
Ejemplos simples:
App.apex.hide("P1_TOTAL");
App.apex.show("P1_TOTAL");
App.apex.setValue("P1_STATUS", "READY");
const status = App.apex.getValue("P1_STATUS");
Esto se vuelve tu “API” interna para manipular items. Ordenado y predecible.
Paso 4: Separar “helpers” de “reglas” con App.rules
Una buena práctica: no mezcles el “cómo” (APEX APIs) con el “qué” (reglas del negocio/UI).
Por eso creamos otro módulo:
ns("App.rules");
App.rules.toggleFields = function() {
const type = App.apex.getValue("P1_TYPE");
if (type === "A") {
App.apex.show("P1_FIELD_A");
App.apex.hide("P1_FIELD_B");
} else {
App.apex.hide("P1_FIELD_A");
App.apex.show("P1_FIELD_B");
}
};
Uso
App.rules.toggleFields();
Esto te permite:
- Mantener la lógica de UI en un solo lugar
- Reutilizar reglas entre páginas (si te conviene)
- Evitar que tus Dynamic Actions se vuelvan “mini programas” incontrolables
Dónde poner este código en APEX
Opciones típicas:
- Global JavaScript File (lo más limpio si aplica a varias páginas)
- Page JavaScript (Function and Global Variable Declaration) si es solo para una página
- Static Application Files si quieres versionarlo y tener control de cambios
Para un patrón namespace, lo ideal es que la base (ns + App.apex) viva de forma central, y las reglas (App.rules) puedan vivir por página o por módulo, según tu arquitectura.
Qué ganas con esto (en serio)
- Menos caos: nada de funciones sueltas en el global
- Escala: tu JS crece por módulos, no por “parches”
- Mantenibilidad: encuentras todo en
App.* - Consistencia: todas las páginas llaman lo mismo para hacer lo mismo
Siguiente nivel (si quieres hacerlo aún más pro)
Una vez tengas App.apex como base, normalmente el siguiente paso es agregar:
App.apex.process(...)como wrapper paraapex.server.processApp.ui.toast(...)para mensajes consistentesApp.util.debounce(...)para inputs y filtros- Un estándar para archivos y nombres por módulo
Pero el punto clave es este: si hoy ordenas el acceso a APEX items con un namespace, mañana tu app deja de depender del “developer de turno” y empieza a parecer un producto serio.