Sintaxis práctica de Mirage Templates para gestionar el diseño
Límite de la capa de plantillas
Mirage está destinado a la representación HTML y textual. Permite reordenar secciones, cambiar tarjetas y encabezados, añadir contenedores decorativos, gestionar clases y mostrar listas preparadas y etiquetas localizadas. Una plantilla no debe ejecutar SQL, determinar permisos, modificar datos ni sustituir la comprobación de rutas del módulo. Si un campo necesario no existe en el contexto de entrada, no puede crearse de forma fiable modificando únicamente el diseño.
Las plantillas públicas se encuentran en skins/<Layout>/<Skin>/<Module>/, las administrativas en adminskins/... y las de moderación en moderatorskins/.... Antes de realizar un cambio, determine los valores activos de Layout y Skin. El mismo nombre de archivo puede aparecer en varios árboles, y editar una copia inactiva no cambiará la página.
Salida con escape
La forma de salida principal y segura utiliza dobles llaves:
<h1>{{ Context.Table("page").Data("title") }}</h1>
<p>{{ Context.Table("page").Data("summary") }}</p>El texto se escapa para HTML, por lo que los caracteres de los datos no se convierten en etiquetas o atributos. Utilice esta forma para títulos, nombres, URL dentro de atributos, etiquetas, cadenas de búsqueda y cualquier valor que pueda haber introducido un usuario.
La salida raw se indica con un signo de exclamación:
{{! Former.BuildHTML(Context.Table("form").Data("submit_ir")) }}Solo se admite para una fuente que ya sea responsable de producir HTML seguro, como un elemento creado por Former, una zona de Echelon o JSON-LD serializado previamente. No cambie una salida normal por raw para hacer que «funcione una etiqueta» procedente de un campo de usuario. Eso convierte los datos en marcado activo.
Tablas con nombre de Context
Para las plantillas nuevas, utilice Context.Table. Una página puede recibir las tablas meta, page, assets y tablas de módulos con el prefijo __. Las llamadas principales son:
Context.HasTable("items"): comprueba si existe una tabla;Context.Table("items").Data("title"): lee un campo de la fila actual;RowCount(): obtiene el número de filas;Rewind(): devuelve el cursor al principio;NextRow(): avanza a la fila siguiente.
La sintaxis compatible Table.Data(...) se mantiene en plantillas antiguas de una sola tabla, pero no debe mezclarse sin motivo con un contexto con nombre.
Condiciones, valores y comentarios
{% if Context.Table("page").Data("status") == "published" %}
<span class="badge badge--live">{{ Boardwords("status_published") | default("Publicado") }}</span>
{% elif Context.Table("page").Data("status") == "draft" %}
<span class="badge badge--draft">{{ Boardwords("status_draft") | default("Borrador") }}</span>
{% else %}
<span class="badge">{{ Boardwords("status_unknown") | default("Desconocido") }}</span>
{% endif %}Los indicadores suelen recibirse como las cadenas "1" y "0", así que compárelos explícitamente. Para una variable temporal se utiliza {% set name = value %}, mientras que un comentario se escribe como {# explicación #} y no aparece en el HTML.
Para un valor de reserva pueden utilizarse dos formas:
{{ Context.Table("meta").Data("title") ?? "Sin título" }}
{{ Boardwords("catalog_title") | default("Catálogo") }}Una expresión ternaria es adecuada para un valor de clase corto, pero los bloques grandes deben expresarse mediante un if normal:
{{ Context.Table("page").Data("compact") == "1" ? "card--compact" : "card--wide" }}Bucle seguro por las filas
Una tabla con nombre no se convierte automáticamente en un array de objetos. Utilice el patrón de cursor completo:
{% if Context.Table("products").RowCount() > 0 %}
{% set __ = Context.Table("products").Rewind() %}
<div class="product-grid">
{% for _ in range(0, Context.Table("products").RowCount(), 1) %}
{% if Context.Table("products").NextRow() %}
<article class="product-card{% if loop.even %} product-card--even{% endif %}">
<h3>{{ Context.Table("products").Data("title") }}</h3>
<p>{{ Context.Table("products").Data("summary") }}</p>
</article>
{% endif %}
{% endfor %}
</div>
{% else %}
<p>{{ Boardwords("products_empty") | default("Todavía no hay nada") }}</p>
{% endif %}El orden es obligatorio: comprobar las filas, ejecutar Rewind(), recorrer el intervalo y llamar a NextRow() antes de leer Data(). Dentro del bucle están disponibles loop.index, loop.first, loop.last, loop.odd y loop.even.
Localización, formularios y zonas
Muestre las etiquetas estáticas destinadas al usuario mediante Boardwords con un fallback comprensible. Así conservan la posibilidad de traducirse al copiar el diseño entre skin trees. Si un campo de formulario se recibe como *_ir, no construya el input manualmente:
<label>{{ Boardwords("profile_name") | default("Nombre del perfil") }}</label>
{{! Former.BuildHTML(Context.Table("profile").Data("name_ir"), "class", "profile-form__input") }}Mirage gestiona el contenedor, la cuadrícula y la clase, mientras que Former gestiona el control HTML. El contenido de una Echelon Zone también se muestra como raw únicamente porque la zona es un productor de fragmentos de confianza; un campo arbitrario de una tabla no tiene ese estatus.
Comprobar el cambio
- Realice un cambio limitado en la plantilla activa, sin copiar todo el shell.
- Compruebe la página con datos, con una lista vacía y sin los campos opcionales.
- Compruebe los caracteres especiales en texto y atributos.
- Compruebe todos los idiomas disponibles y el fallback de Boardwords.
- Compruebe desktop y mobile y, en un formulario administrativo, los errores de validación.
- Asegúrese de que raw se utilice únicamente con productores de HTML de confianza.
Si la plantilla necesita nuevos permisos, SQL o una regla de negocio, deténgase: la tarea ha sobrepasado el límite de Mirage. Una ampliación correcta prepara primero los datos en el módulo y mantiene la plantilla como una capa de presentación predecible.
Composición de la página y fragmentos dinámicos
Edite por separado el shell común, el menú y el contenido del módulo de aplicación. No traslade todo el armazón a la plantilla de una sola tarjeta: genera duplicación y rompe el cambio de skin. Para un fragmento que se actualiza mediante un filtro, pager o pestaña, utilice el entorno AJAX establecido con Mirage. Mantenga el texto público canónico, importante para la primera visualización y el SEO, en el renderizado normal de la página.
Si la plantilla utiliza un namespace adicional de un paquete de callbacks, por ejemplo para formatear una fecha, compruebe que el paquete esté presente en el runtime de destino. Ese callback es una dependencia de la plantilla concreta. Proporcione un fallback comprensible cuando el diseño lo permita y no intente imitar un callback ausente mediante expresiones de cadena complejas.
Práctica de una revisión segura
Compare el HTML final antes y después del cambio, no solo el archivo fuente .mirage. Compruebe el equilibrio de etiquetas, la unicidad de los id, la accesibilidad de los label, la validez de los enlaces y la ausencia de elementos interactivos anidados. En las listas, pruebe una fila y varias: los errores de cursor suelen quedar ocultos con un único registro. Al trasladar una plantilla entre skin trees, vuelva a comprobar los nombres de tablas y campos, ya que dos módulos visualmente parecidos no tienen por qué recibir el mismo contexto.