Office Address

  • 123/A, Miranda City Prikano
  • +0989 7876 9865 9
  • info@example.com

Social List

Cuando usar cada uno de los substitution strings en Oracle APEX Templates

Lectura: 5 minutos

Si llevas un tiempo trabajando con Oracle APEX, ya sabes que los templates (plantillas) son el corazón de cómo APEX construye visualmente cada página. Pero más allá de simplemente elegir un tema bonito desde el Theme Roller, hay un mecanismo interno enormemente poderoso que vale la pena dominar: las sustituciones en templates. En este post vamos a desmontarlo por completo, con ejemplos reales de código.


¿Qué es un template en Oracle APEX?

Un template en APEX es esencialmente un bloque de HTML con marcadores especiales llamados substitution strings (cadenas de sustitución). Cuando APEX renderiza una página, toma esos marcadores y los reemplaza con valores reales: el nombre de un ítem, el título de una región, el valor de una columna en un reporte, etc.

Piénsalo así: es exactamente el mismo concepto que un template de correo electrónico donde escribes “Hola, {NOMBRE}” y el sistema lo reemplaza con el nombre real de cada destinatario. APEX hace lo mismo, pero a escala de toda la interfaz de tu aplicación.

Existen dos sintaxis para hacer estas sustituciones, y es importante entender cuándo usar cada una.


Sintaxis de sustitución: placeholder vs session state

Placeholder substitution (#NOMBRE#)

Esta sintaxis usa el símbolo # para encerrar el nombre del marcador. Se usa dentro de la definición de un template para referenciar los atributos del componente que ese template representa. Por ejemplo, dentro del template de una región, #TITLE# se reemplaza con el título de esa región.

<!-- Ejemplo: Template personalizado de región -->
<div class="mi-region-card">
  <div class="mi-region-card__header">
    <h2 class="mi-region-card__title">#TITLE#</h2>
  </div>
  <div class="mi-region-card__body">
    #BODY#
  </div>
  <div class="mi-region-card__footer">
    #CHANGE#
  </div>
</div>

Aquí #TITLE#, #BODY# y #CHANGE# son placeholders que APEX reemplaza cuando construye la página. Cada tipo de template (page, region, report, etc.) tiene su propio conjunto de placeholders disponibles.

Session state substitution (&ITEM.)

Esta sintaxis usa & al inicio y . al final. Referencia el valor en Session State de un ítem de página o de aplicación. La puedes usar prácticamente en cualquier campo de texto dentro del App Builder: títulos de regiones, mensajes, atributos HTML, código JavaScript, etc.

html

<!-- Usar el valor de un ítem de página directamente en HTML -->
<p>Bienvenido de nuevo, <strong>&APP_USER.</strong></p>
<p>Estás consultando datos del cliente: <strong>&P10_CLIENTE_NOMBRE.</strong></p>

La diferencia clave es que #PLACEHOLDER# solo funciona dentro de la definición del template, mientras que &ITEM. funciona en casi cualquier parte del App Builder donde puedas escribir texto.


Escape filters: seguridad integrada en las sustituciones

Uno de los aspectos más importantes y frecuentemente ignorados de las sustituciones es el escape de caracteres. Si un usuario escribe <script>alert('XSS')</script> en un campo y tú lo muestras sin escapar, tienes una vulnerabilidad Cross-Site Scripting (XSS).

APEX lo resuelve con filtros que se añaden con ! después del nombre del ítem:

<!-- Sin filtro - PELIGROSO si el valor viene de input del usuario -->
<div>#COMENTARIO!RAW#</div>

<!-- Escapado para HTML - reemplaza < > & " con sus entidades HTML -->
<div>#COMENTARIO!HTML#</div>

<!-- Escapado para atributos HTML - para usar dentro de atributos de tags -->
<input type="text" placeholder="#PLACEHOLDER_TEXT!ATTR#" />

<!-- Escapado para JavaScript - para inyectar valores en código JS -->
<script>
  var nombre = "&P1_NOMBRE!JS.";
  apex.message.showPageSuccess("Guardado: " + nombre);
</script>

<!-- Eliminar HTML del valor y luego escaparlo -->
<span title="#DESCRIPCION!STRIPHTML#">#DESCRIPCION!HTML#</span>

La regla práctica es simple: usa !HTML para contenido en el cuerpo de la página, !ATTR para atributos de tags HTML, !JS cuando inyectas en JavaScript, y !RAW solo cuando confías completamente en el origen del dato (por ejemplo, HTML que tú mismo construiste en PL/SQL).


Built-in substitution strings: el conjunto de variables globales de APEX

APEX tiene un conjunto de variables globales que puedes usar en cualquier parte de tu aplicación sin necesidad de crearlas tú mismo. Estas son algunas de las más útiles:

<!-- Información de la aplicación y sesión -->
<p>App ID: &APP_ID.</p>
<p>Page ID: &APP_PAGE_ID.</p>
<p>Session: &APP_SESSION.</p>
<p>Usuario actual: &APP_USER.</p>

<!-- Ideal para construir URLs dinámicas -->
<a href="f?p=&APP_ID.:10:&APP_SESSION.::NO::P10_ID:&P1_ID.">
  Ver detalle
</a>

<!-- Fecha y hora del servidor -->
<p>Fecha actual: &SYSDATE_YYYYMMDD.</p>

En el contexto de un reporte SQL, también tienes acceso a las columnas directamente como sustituciones dentro del template de las filas.

<!-- Template de una fila en un Classic Report personalizado -->
<tr class="fila-cliente #STATUS_CSS_CLASS!ATTR#">
  <td>#CLIENTE_ID!HTML#</td>
  <td>#NOMBRE!HTML#</td>
  <td>
    <span class="badge badge--#ESTATUS!ATTR#">#ESTATUS!HTML#</span>
  </td>
  <td>
    <a href="f?p=&APP_ID.:20:&APP_SESSION.::NO::P20_ID:#CLIENTE_ID!ATTR#">
      Ver Portal
    </a>
  </td>
</tr>

Text messages con sustituciones: i18n en APEX

Si tu aplicación necesita soporte multiidioma o simplemente quieres centralizar los textos en un solo lugar para facilitar el mantenimiento APEX tiene el mecanismo de Text Messages (Shared Components → Text Messages). Puedes referenciarlos con la sustitución especial APP_TEXT$:

-- Primero defines el mensaje en Shared Components con el nombre MSG_BIENVENIDA
-- Valor: "Bienvenido al Portal de APEX, {0}"
<!-- En cualquier template o atributo de texto -->
&APP_TEXT$MSG_BIENVENIDA!HTML.

<!-- Para un idioma específico (NL = Dutch, ES = Spanish, etc.) -->
&APP_TEXT$MSG_BIENVENIDA$ES!HTML.

También puedes hacer sustituciones posicionales dentro de los mensajes usando APEX_LANG.MESSAGE:

-- En PL/SQL, para construir el mensaje con el valor del ítem
DECLARE
  l_mensaje VARCHAR2(500);
BEGIN
  l_mensaje := APEX_LANG.MESSAGE(
    p_name  => 'MSG_BIENVENIDA',
    p0      => :APP_USER
  );
  -- l_mensaje = "Bienvenido a APEX"
END;

JavaScript en templates: apex.util.applyTemplate

Este es probablemente el tema más avanzado y menos documentado de los templates básicos. APEX expone una función JavaScript que te permite evaluar templates del lado del cliente, lo cual es muy útil cuando construyes interfaces dinámicas con Dynamic Actions o plugins.

// Ejemplo práctico: construir HTML dinámico en un Dynamic Action
// o en el JavaScript de inicialización de una región

var template = '<div class="info-card">' +
               '  <h3 class="info-card__title">{TITULO}</h3>' +
               '  <p class="info-card__body">{DESCRIPCION}</p>' +
               '  <a href="{URL}" class="info-card__link">Ver más</a>' +
               '</div>';

var datos = {
  TITULO:      "Cliente Activo",
  DESCRIPCION: "Este cliente tiene 3 órdenes pendientes de aprobación.",
  URL:         "f?p=" + apex.env.APP_ID + ":20:" + apex.env.APP_SESSION + "::NO::P20_ID:1042"
};

// applyTemplate reemplaza {CLAVE} con los valores del objeto de datos
var htmlResultante = apex.util.applyTemplate(template, { placeholders: datos });

// Inyectamos el resultado en el DOM
apex.jQuery("#mi-contenedor-dinamico").html(htmlResultante);

Este patrón es especialmente poderoso cuando recibes datos de un proceso AJAX (por ejemplo, desde apex.server.process) y necesitas renderizar HTML en el cliente sin hacer un full page refresh:

// Llamada AJAX al servidor
apex.server.process("OBTENER_INFO_CLIENTE", {
  x01: apex.item("P10_CLIENTE_ID").getValue()
}, {
  success: function(pData) {
    
    var template = 
      '<div class="cliente-card">' +
      '  <span class="cliente-card__rfc">{RFC}</span>' +
      '  <span class="cliente-card__nombre">{NOMBRE}</span>' +
      '  <span class="badge badge--{ESTATUS_CSS}">{ESTATUS}</span>' +
      '</div>';

    // pData viene de APEX_JSON en el proceso PL/SQL
    var html = apex.util.applyTemplate(template, {
      placeholders: {
        RFC:        pData.rfc,
        NOMBRE:     pData.nombre,
        ESTATUS_CSS: pData.estatus.toLowerCase(),
        ESTATUS:    pData.estatus
      }
    });

    apex.jQuery("#region-info-cliente").html(html);
  }
});

Y el proceso PL/SQL correspondiente en el servidor:

-- Proceso tipo "PL/SQL Code" invocado vía apex.server.process
DECLARE
  l_cliente_id NUMBER := apex_application.g_x01;
BEGIN
  FOR r IN (
    SELECT rfc, nombre, estatus
    FROM   clientes
    WHERE  id = l_cliente_id
  ) LOOP
    APEX_JSON.open_object;
    APEX_JSON.write('rfc',     r.rfc);
    APEX_JSON.write('nombre',  r.nombre);
    APEX_JSON.write('estatus', r.estatus);
    APEX_JSON.close_object;
  END LOOP;
END;

Resumen: cuándo usar cada mecanismo

Entender cuándo aplicar cada tipo de sustitución te ahorrará horas de depuración y te permitirá escribir templates más seguros y mantenibles.

Usa #PLACEHOLDER# cuando estés editando la definición de un template en Shared Components y quieras referenciar los atributos del componente (título, cuerpo, botones de acción, etc.).

Usa &ITEM. cuando quieras inyectar el valor en Session State de cualquier ítem de página o aplicación en títulos, mensajes, atributos, o cualquier campo de texto del App Builder.

Usa siempre un escape filter (!HTML, !ATTR, !JS) que corresponda al contexto donde renderizas el valor. El único momento para usar !RAW es cuando el contenido HTML lo generaste tú mismo en PL/SQL de forma controlada.

Usa APP_TEXT$ para centralizar textos en Text Messages, especialmente si manejas múltiples idiomas o simplemente quieres poder cambiar textos sin tocar el template.

Y usa apex.util.applyTemplate en JavaScript cuando necesites renderizar HTML dinámicamente en el cliente, especialmente en combinación con apex.server.process para interfaces reactivas sin refresh de página.


Dominar estas herramientas eleva tu nivel en desarrollo y seguridad: en lugar de depender únicamente de los templates predeterminados del Universal Theme, puedes construir exactamente la experiencia visual que tu aplicación necesita, con toda la potencia del motor de templates de APEX detrás.

Post a Comment

Your email address will not be published. Required fields are marked *