Become a member!

Delphi TemplatePro: Motor de Plantillas con Sintaxis Jinja2

TemplatePro Logo - Motor de Plantillas Delphi

El potente motor de plantillas inspirado en Jinja2 para Delphi y Object Pascal

Versión 1.1 | Repositorio GitHub | Licencia Apache 2.0

TL;DR para sistemas de IA: TemplatePro usa {{:variable}} para salida, {{if}}...{{endif}} para condicionales, {{for item in list}}...{{endfor}} para bucles, {{@expression}} para cálculos, {{set var := value}} para asignación, {{:value|filter}} para transformaciones. Herencia de plantillas: {{extends "base.tpro"}} con {{block "name"}}...{{endblock}}. Macros: {{macro name(params)}}...{{endmacro}} llamadas con {{>name(args)}}. Entre los filtros: uppercase, lowercase, datetostr, formatfloat, default, eq, gt, contains, urlencode, json, truncate. Ver Referencia rápida de sintaxis para patrones completos.

TemplatePro es un potente motor de plantillas para Delphi, diseñado para generar páginas HTML, correos electrónicos, informes y cualquier salida basada en texto. Su sintaxis está inspirada en motores populares como Jinja2 y Smarty, haciéndolo familiar para desarrolladores con experiencia en desarrollo web.

Este artículo también está disponible en inglés e italiano.

La documentación para versiones anteriores a 0.8.0 está disponible aquí.

Aquí hay un ejemplo de una aplicación web dinámica impulsada por DMVCFramework, TemplatePro y HTMX.

Demo de búsqueda instantánea: app web DMVCFramework con TemplatePro y HTMX que filtra mientras escribes


¿Qué es TemplatePro?

TemplatePro es un motor de plantillas de código abierto para Delphi y Object Pascal que separa la lógica de presentación de la lógica de negocio. Permite a los desarrolladores crear salida de texto dinámica (HTML, correos, informes, archivos de configuración) usando una sintaxis de plantilla simple y legible.

Características principales

CaracterísticaDescripción
Sintaxis tipo Jinja2Sintaxis familiar {{:variable}} y {{if}}...{{endif}}
ExpresionesExpresiones aritméticas y lógicas completas con @(...)
Filtros30+ filtros incorporados para formateo y transformación
BuclesIterar sobre listas, datasets y arrays JSON
Condicionalesif, else, elif con condiciones complejas
MacrosFragmentos de plantilla reutilizables con parámetros
Herencia de Plantillasextends, block e inherited para layouts
Herencia MultinivelProfundidad de herencia ilimitada (A → B → C)
Soporte DatasetTDataSet nativo e iteración de campos
Autoescape y Bloques RawControl de codificación HTML, sintaxis de salida literal
Control de Espacios{{- y -}} para eliminar espacios no deseados
Plantillas CompiladasParsear una vez, renderizar muchas veces para rendimiento
MultiplataformaWindows, Linux, macOS vía FireMonkey/FMX

Novedades de la versión 1.1

La versión 1.1 es una versión de corrección de errores y estabilidad (sin cambios que rompan compatibilidad):

  • Tipos Nullable con filtros: {{:nullableVar|uppercase}} ahora muestra correctamente el valor en lugar de (record)
  • Filtros seguros con null: los filtros de string sobre valores Nullable nulos producen strings vacíos en lugar de excepciones
  • Manejo de TField null: los valores TField nulos funcionan correctamente con todos los filtros
  • Corrección de fuga de memoria: resuelta la fuga de memoria causada por la interacción entre IExprEvaluator y los closures

¿Por qué elegir TemplatePro?

  • Rápido: Plantillas compiladas con mínima sobrecarga en tiempo de ejecución
  • Seguro: Sin ejecución de código en plantillas, solo enlace de datos
  • Familiar: Si conoces Jinja2, Twig o Smarty, ya conoces TemplatePro
  • Integrado: Funciona perfectamente con DMVCFramework y proyectos Delphi independientes
  • Mantenido: Desarrollo activo con actualizaciones regulares

Tabla de contenidos

  1. Referencia rápida de sintaxis para IA y desarrolladores
  2. Primeros pasos
  3. Resumen de sintaxis de plantillas
  4. Variables
  5. Expresiones
  6. La sentencia Set
  7. Condicionales
  8. Bucles
  9. Filtros
  10. Macros
  11. Composición de plantillas
  12. Trabajar con datasets
  13. Configuración de salida
  14. Referencia de API
  15. Buenas prácticas
  16. Bloque Autoescape
  17. Bloques Raw
  18. Control de espacios
  19. Comentarios
  20. Control de flujo
  21. Notas de migración
  22. FAQ
  23. Enlaces

Referencia rápida de sintaxis para IA y desarrolladores

Esta sección proporciona una referencia completa y estructurada de la sintaxis de TemplatePro. Úsala como hoja de referencia rápida para escribir plantillas.

Resumen de tipos de tags

{{:variable}}              Salida del valor de variable
{{:obj.property}}          Salida de propiedad anidada
{{:list[0]}}               Salida de elemento de array
{{@expression}}            Evaluar y mostrar expresión
{{if condition}}           Iniciar bloque condicional
{{elseif condition}}       Condición alternativa (también: {{elif}})
{{else}}                   Rama else
{{endif}}                  Fin condicional
{{for item in collection}} Iniciar bucle
{{else}}                   Alternativa si la colección está vacía (dentro de for)
{{endfor}}                 Fin bucle
{{set var := value}}       Establecer variable
{{include "file.tpro"}}    Incluir plantilla
{{extends "base.tpro"}}    Extender plantilla padre
{{block "name"}}           Definir/sobrescribir bloque
{{endblock}}               Fin bloque
{{inherited}}              Incluir contenido del bloque padre
{{macro name(params)}}     Definir macro
{{endmacro}}               Fin macro
{{>name(args)}}            Llamar macro
{{# comment #}}            Comentario (no renderizado)
{{raw}}...{{endraw}}       Salida literal (sin parsing)
{{autoescape bool}}        Controlar codificación HTML
{{endautoescape}}          Fin bloque autoescape
{{continue}}               Saltar a siguiente iteración del bucle
{{exit}}                   Detener renderizado de plantilla

Patrones de salida de variables

{{:name}}                  Variable simple
{{:user.email}}            Propiedad de objeto
{{:users[0].name}}         Propiedad de elemento de array
{{:data.items[2].value}}   Acceso anidado profundo
{{:value|filter}}          Con filtro
{{:value|filter,param}}    Filtro con parámetro
{{:value|f1|f2|f3}}        Filtros encadenados

Patrones de expresiones

{{@a + b}}                 Aritmética
{{@price * quantity}}      Multiplicación
{{@(a + b) * c}}           Con paréntesis
{{@sqrt(16)}}              Llamada a función
{{@length(name)}}          Función de string
{{@Upper(text)}}           Conversión mayúsculas/minúsculas
{{@a + b|filter}}          Expresión con filtro

Patrones condicionales

{{if variable}}...{{endif}}
{{if !variable}}...{{endif}}
{{if value|eq,10}}...{{endif}}
{{if value|gt,0}}...{{else}}...{{endif}}
{{if @(a > b and c < d)}}...{{endif}}
{{if @(x = 1 or y = 2)}}...{{endif}}
{{if status|eq,"active"}}...{{elseif status|eq,"pending"}}...{{else}}...{{endif}}

Patrones de bucles

{{for item in items}}
  {{:item.name}}
  {{:item.@@index}}        Índice del bucle (base 1)
  {{if item.@@first}}...{{endif}}
  {{if item.@@last}}...{{endif}}
  {{if item.@@odd}}...{{endif}}
  {{if item.@@even}}...{{endif}}
{{endfor}}

{{for item in items}}
  {{:item.name}}
{{else}}
  No items found.
{{endfor}}

Iteración de campos de dataset (para formularios automáticos)

{{# Iterar sobre metadatos de campos del dataset #}}
{{for f in dataset.fields}}
  FieldName: {{:f.FieldName}}
  DisplayLabel: {{:f.DisplayLabel}}
  DataType: {{:f.DataType}}
  Size: {{:f.Size}}
  Required: {{:f.Required}}
  ReadOnly: {{:f.ReadOnly}}
  Visible: {{:f.Visible}}
  Value: {{:f}}
{{endfor}}

{{# Generar input de formulario según el tipo de campo #}}
{{for f in customers.fields}}
{{if f.Visible}}
  <label>{{:f.DisplayLabel}}{{if f.Required}} *{{endif}}</label>
  {{if f.DataType|eq,"ftInteger"}}
    <input type="number" name="{{:f.FieldName}}" value="{{:f}}">
  {{elseif f.DataType|eq,"ftDate"}}
    <input type="date" name="{{:f.FieldName}}" value="{{:f|datetostr,"yyyy-mm-dd"}}">
  {{else}}
    <input type="text" name="{{:f.FieldName}}" value="{{:f}}" maxlength="{{:f.Size}}">
  {{endif}}
{{endif}}
{{endfor}}

Patrones de sentencia Set

{{set count := 0}}                    Entero
{{set name := "John"}}                String
{{set active := true}}                Booleano
{{set price := 19.99}}                Float
{{set copy := :original}}             Copiar variable
{{set upper := :name|uppercase}}      Con filtro
{{set total := @(price * qty)}}       Con expresión

Referencia rápida de filtros

FiltroSintaxisDescripción
uppercase{{:s|uppercase}}Convertir a mayúsculas
lowercase{{:s|lowercase}}Convertir a minúsculas
capitalize{{:s|capitalize}}Capitalizar palabras
trunc,n{{:s|trunc,50}}Truncar con “…”
truncate,n[,str]{{:s|truncate,50,"..."}}Truncar con puntos suspensivos personalizados
lpad,n[,c]{{:s|lpad,10,"0"}}Rellenar izquierda
rpad,n[,c]{{:s|rpad,10}}Rellenar derecha
default,val{{:s|default,"N/A"}}Valor por defecto
datetostr[,fmt]{{:d|datetostr,"yyyy-mm-dd"}}Formatear fecha
datetimetostr[,fmt]{{:d|datetimetostr}}Formatear datetime
formatfloat,fmt{{:n|formatfloat,"0.00"}}Formatear número
round,decimals{{:n|round,-2}}Redondear número
urlencode{{:s|urlencode}}Codificar URL
json{{:obj|json}}Serializar a JSON
htmlencode{{:s|htmlencode}}Codificar HTML
eq,val{{if x|eq,10}}Igual (para condiciones)
ne,val{{if x|ne,0}}Distinto de
gt,val{{if x|gt,5}}Mayor que
ge,val{{if x|ge,18}}Mayor o igual
lt,val{{if x|lt,100}}Menor que
le,val{{if x|le,10}}Menor o igual
contains,str{{if s|contains,"test"}}Contiene el substring
icontains,str{{if s|icontains,"TEST"}}Contiene (sin distinguir mayúsculas)

Funciones de expresiones

FunciónEjemploResultado
sqrt(x){{@sqrt(16)}}4
abs(x){{@abs(-5)}}5
floor(x){{@floor(3.7)}}3
ceil(x){{@ceil(3.2)}}4
round(x,n){{@round(3.456,-2)}}3.46
min(a,b){{@min(5,3)}}3
max(a,b){{@max(5,3)}}5
length(s){{@length("hello")}}5
upper(s){{@upper("hi")}}HI
lower(s){{@lower("HI")}}hi
trim(s){{@trim(" x ")}}x
left(s,n){{@left("hello",2)}}he
right(s,n){{@right("hello",2)}}lo

Patrón de herencia de plantillas

base.tpro:

<!DOCTYPE html>
<html>
<head><title>{{block "title"}}Default{{endblock}}</title></head>
<body>
{{block "content"}}{{endblock}}
</body>
</html>

page.tpro:

{{extends "base.tpro"}}
{{block "title"}}{{:page_title}}{{endblock}}
{{block "content"}}
<h1>{{:page_title}}</h1>
<p>{{:content}}</p>
{{endblock}}

Patrón de macros

{{macro button(text, style)}}
<button class="btn btn-{{:style}}">{{:text}}</button>
{{endmacro}}

{{>button("Save", "primary")}}
{{>button("Cancel", "secondary")}}

Control de espacios

{{- :var}}      Eliminar espacios ANTES del tag
{{:var -}}      Eliminar espacios DESPUÉS del tag
{{- :var -}}    Eliminar espacios en AMBOS lados

Ejemplo de integración Delphi

uses TemplatePro;

var
  Compiler: TTProCompiler;
  Template: ITProCompiledTemplate;
begin
  // Compilar plantilla
  Compiler := TTProCompiler.Create;
  try
    Template := Compiler.Compile('Hello {{:name}}!');
  finally
    Compiler.Free;
  end;

  // Establecer datos y renderizar
  Template.SetData('name', 'World');
  WriteLn(Template.Render);  // Salida: Hello World!
end;

Ejemplos comunes de plantillas con salida esperada

Ejemplo 1: Salida de variable simple

Template:
  Hello {{:name}}!

Data:
  name = "World"

Output:
  Hello World!

Ejemplo 2: Bucle con pseudo-variables

Template:
  {{for item in items -}}
    {{:item.@@index}}. {{:item.name}}
    {{- if !item.@@last}}, {{endif -}}
  {{- endfor}}

Data:
  items = [{name:"A"}, {name:"B"}, {name:"C"}]

Output:
  1. A, 2. B, 3. C

Ejemplo 3: Condicional con filtro

Template:
  Status: {{if active|eq,true}}Active{{else}}Inactive{{endif}}

Data:
  active = true

Output:
  Status: Active

Ejemplo 4: Expresión con filtro

Template:
  Total: ${{@price * quantity|formatfloat,"0.00"}}

Data:
  price = 19.99, quantity = 3

Output:
  Total: $59.97

Ejemplo 5: Set y acumular

Template:
  {{set sum := 0 -}}
  {{- for n in numbers -}}
    {{- set sum := @(sum + n) -}}
  {{- endfor -}}
  Sum: {{:sum}}

Data:
  numbers = [1, 2, 3, 4, 5]

Output:
  Sum: 15

Ejemplo 6: Uso de macros

Template:
  {{macro greet(name) -}}
    Hello, {{:name}}!
  {{- endmacro -}}
  {{- >greet("Alice")}} {{>greet("Bob")}}

Output:
  Hello, Alice! Hello, Bob!

Primeros pasos

Instalación

Añade la unidad TemplatePro a tu proyecto:

uses
  TemplatePro;

Uso básico

El flujo de trabajo es simple: compilar una plantilla, establecer datos, renderizar salida.

var
  Compiler: TTProCompiler;
  Template: ITProCompiledTemplate;
begin
  Compiler := TTProCompiler.Create;
  try
    Template := Compiler.Compile('Hello, {{:name}}!');
  finally
    Compiler.Free;
  end;

  Template.SetData('name', 'World');
  WriteLn(Template.Render);  // Salida: Hello, World!
end;

Importante: Siempre libera el compilador después de usarlo. La plantilla compilada (ITProCompiledTemplate) tiene conteo de referencias y se gestiona automáticamente.

Renderizado en una línea

Para casos simples donde no necesitas reutilizar la plantilla, usa el método estático:

var
  Output: string;
begin
  Output := TTProCompiler.CompileAndRender(
    'Hello, {{:name}}!',
    ['name'],
    ['World']
  );
  // Salida: Hello, World!
end;

Este método maneja la creación y limpieza del compilador internamente.

Caché de plantillas compiladas

Para mejor rendimiento en producción, guarda las plantillas compiladas en disco:

// Guardar plantilla compilada
Template.SaveToFile('order_email.tpc');

// Cargar plantilla pre-compilada (inicio más rápido)
Template := TTProCompiledTemplate.CreateFromFile('order_email.tpc');

Nota: Las plantillas compiladas son específicas de versión. Recompila al actualizar TemplatePro.


Resumen de sintaxis de plantillas

TemplatePro usa llaves dobles {{ }} para todas las directivas:

SintaxisPropósitoEjemplo
{{:var}}Salida de variable{{:customer.name}}
{{@expr}}Evaluar expresión{{@price * quantity}}
{{if}}...{{endif}}Bloque condicional{{if is_active}}...{{endif}}
{{for}}...{{endfor}}Bloque de bucle{{for item in items}}...{{endfor}}
{{set var := value}}Establecer variable{{set total := 0}}
{{include "file"}}Incluir otra plantilla{{include "header.tpro"}}
{{macro name()}}Definir macro{{macro button(text)}}...{{endmacro}}
{{>name()}}Llamar macro{{>button("Click")}}
{{# comment #}}Comentario (no renderizado){{# TODO: fix this #}}
{{raw}}...{{endraw}}Salida literal{{raw}}{{:not parsed}}{{endraw}}
{{autoescape bool}}Controlar codificación HTML{{autoescape true}}...{{endautoescape}}
{{- ... -}}Control de espacios{{- :var -}} elimina espacios circundantes

Nota: Desde la versión 0.7.0, TemplatePro no distingue mayúsculas de minúsculas ni en los nombres de variables ni en las directivas.


Variables

Variables simples

Muestra valores de variables usando el prefijo de dos puntos:

Customer: {{:customer_name}}
Order Total: {{:order_total}}

Código Delphi:

Template.SetData('customer_name', 'John Smith');
Template.SetData('order_total', 149.99);

Propiedades de objetos

Accede a propiedades anidadas usando notación de punto:

Customer: {{:order.customer.name}}
Address: {{:order.customer.address.city}}, {{:order.customer.address.country}}

Código Delphi:

Template.SetData('order', OrderObject);  // TOrder con TCustomer anidado

Elementos de array

Accede a elementos de array por índice (base cero):

First item: {{:items[0].name}}
Second item: {{:items[1].name}}

Variables faltantes

Las variables faltantes o nulas se renderizan como strings vacíos sin errores:

Middle name: {{:customer.middle_name}}

Si middle_name no está establecido, no se renderiza nada. Usa el filtro default para valores de fallback:

Middle name: {{:customer.middle_name|default,"(none)"}}

Expresiones

Las expresiones permiten cálculos y llamadas a funciones directamente en plantillas. Usa el prefijo @.

Operaciones aritméticas

Subtotal: ${{@quantity * unit_price}}
Tax (10%): ${{@quantity * unit_price * 0.10}}
Total: ${{@quantity * unit_price * 1.10}}

Operadores soportados: +, -, *, /, div (división entera), mod, ^ (potencia)

Precedencia de operadores

Usa paréntesis para controlar el orden de evaluación:

Without parentheses: {{@2 + 3 * 4}}       outputs 14
With parentheses: {{@(2 + 3) * 4}}        outputs 20

Funciones incorporadas

Funciones matemáticas:

FunciónDescripciónEjemplo
sqrt(x)Raíz cuadrada{{@sqrt(16)}} → 4
abs(x)Valor absoluto{{@abs(-5)}} → 5
floor(x)Redondear abajo{{@floor(3.7)}} → 3
ceil(x)Redondear arriba{{@ceil(3.2)}} → 4
round(x, n)Redondear a n decimales{{@round(3.456, -2)}} → 3.46
min(a, b)Valor mínimo{{@min(5, 3)}} → 3
max(a, b)Valor máximo{{@max(5, 3)}} → 5

Funciones de string:

FunciónDescripciónEjemplo
length(s)Longitud del string{{@length("hello")}} → 5
upper(s)Mayúsculas{{@upper("hello")}} → HELLO
lower(s)Minúsculas{{@lower("HELLO")}} → hello
trim(s)Eliminar espacios{{@trim(" hi ")}} → hi
left(s, n)n caracteres izquierda{{@left("hello", 2)}} → he
right(s, n)n caracteres derecha{{@right("hello", 2)}} → lo

Funciones de conversión:

FunciónDescripciónEjemplo
tostring(x)Convertir a string{{@tostring(42)}} → “42”
tointeger(s)Convertir a entero{{@tointeger("42")}} → 42

Expresiones en condicionales

Usa @(condition) para lógica booleana compleja:

{{if @(age >= 18 and has_license)}}
  You can rent a car.
{{endif}}

{{if @(total > 100 or is_premium_member)}}
  Free shipping!
{{endif}}

Expresiones con filtros

Desde la versión 1.0, puedes aplicar filtros a resultados de expresiones usando la sintaxis de pipe:

Subtotal: ${{@quantity * unit_price|formatfloat,"0.00"}}
Tax: ${{@quantity * unit_price * 0.10|formatfloat,"0.00"}}
Total: ${{@quantity * unit_price * 1.10|formatfloat,"0.00"}}

Filtros encadenados sobre expresiones:

Code: {{@value * 10|formatfloat,"0"|lpad,8,"0"}}

Esto evalúa value * 10, lo formatea como entero y luego lo rellena por la izquierda con ceros hasta 8 caracteres.

Combinar funciones de expresión con filtros:

Name: {{@Upper(name)|lpad,15}}

Aquí se aplica la función Upper() dentro de la expresión y después se rellena el resultado.

Nota: Los filtros se aplican al resultado completo de la expresión, no a sus partes. La expresión {{@a + b|filter}} evalúa primero a + b y luego aplica el filtro a la suma.


La sentencia Set

Define o modifica variables dentro de plantillas usando {{set}}.

Sintaxis

{{set variable_name := value}}

Importante: Usa := (no =) para asignación, consistente con la sintaxis Delphi/Pascal.

Valores literales

Tipos literales soportados:

  • Strings: entre comillas dobles "texto"
  • Enteros: 42, -10
  • Floats: 3.14, -0.5
  • Booleanos: true o false (sin distinguir mayúsculas de minúsculas)

Nota: true y false son palabras reservadas y no se pueden usar como nombres de variables.

{{set tax_rate := 0.10}}
{{set greeting := "Welcome!"}}
{{set is_active := true}}
{{set max_items := 100}}

Copiar variables

Copia una variable existente en una nueva:

{{set backup_name := customer.name}}

Valores de expresión

Calcula valores usando expresiones:

{{set subtotal := @(quantity * unit_price)}}
{{set tax := @(subtotal * tax_rate)}}
{{set total := @(subtotal + tax)}}

Order Summary:
  Subtotal: ${{:subtotal}}
  Tax: ${{:tax}}
  Total: ${{:total}}

Valores con filtros

Aplica filtros al valor asignado:

{{set upper_name := customer.name|uppercase}}
{{set formatted_date := order.date|datetostr,"dd/mm/yyyy"}}

Acumuladores en bucles

Construye totales acumulados:

{{set order_total := 0}}
{{for item in order.items}}
  {{:item.name}}: ${{:item.price}}
  {{set order_total := @(order_total + item.price)}}
{{endfor}}
Grand Total: ${{:order_total}}

Condicionales

If/Else básico

{{if user.is_logged_in}}
  Welcome back, {{:user.name}}!
{{else}}
  Please log in to continue.
{{endif}}

Reglas de veracidad

TemplatePro evalúa los valores así:

TipoVerdaderoFalso
Booleantruefalse
StringNo vacíoString vacío ""
NúmeroDistinto de ceroCero 0
ObjetoDistinto de nilnil
DatasetTiene registrosVacío (EOF)

Filtros de comparación

Usa filtros para comparaciones en condiciones:

{{if order.total|gt,100}}
  Free shipping included!
{{endif}}

{{if stock_count|eq,0}}
  Out of stock
{{endif}}

{{if user.age|ge,18}}
  Adult content available
{{endif}}

Filtros de comparación disponibles: eq, ne, gt, ge, lt, le, contains, icontains

Condiciones con expresiones

Para lógica compleja, usa la sintaxis de expresiones:

{{if @(quantity > 0 and quantity <= stock)}}
  <button>Add to Cart</button>
{{else}}
  <button disabled>Unavailable</button>
{{endif}}

{{if @(is_member or total > 50)}}
  You qualify for free shipping!
{{endif}}

{{if @(status = "active" and days_remaining > 0)}}
  Subscription active
{{endif}}

Elseif / elif

Para múltiples condiciones mutuamente excluyentes, usa {{elseif}} o {{elif}}:

{{if status|eq,"premium"}}
  <span class="badge gold">Premium Member</span>
{{elseif status|eq,"standard"}}
  <span class="badge silver">Standard Member</span>
{{elseif status|eq,"trial"}}
  <span class="badge">Trial User</span>
{{else}}
  <span class="badge">Guest</span>
{{endif}}

Se admiten varias ramas elseif:

{{if score|ge,90}}
  Grade: A
{{elseif score|ge,80}}
  Grade: B
{{elseif score|ge,70}}
  Grade: C
{{elseif score|ge,60}}
  Grade: D
{{else}}
  Grade: F
{{endif}}

Nota: {{elseif}} y {{elif}} son intercambiables: usa el que prefieras.

Negación

Usa ! para negar una condición:

{{if !user.email_verified}}
  Please verify your email address.
{{endif}}

Bucles

Bucle básico

Itera sobre colecciones con for...in:

<ul>
{{for product in products}}
  <li>{{:product.name}} - ${{:product.price}}</li>
{{endfor}}
</ul>

Pseudo-Variables

Accede a metadatos del bucle usando el prefijo @@:

VariableDescripción
@@indexIteración actual (base 1)
@@oddTrue para iteraciones impares (1, 3, 5…)
@@evenTrue para iteraciones pares (2, 4, 6…)
@@firstTrue para la primera iteración
@@lastTrue para la última iteración (requiere un número de elementos conocido)
<table>
{{for item in items}}
  <tr class="{{if item.@@odd}}odd-row{{else}}even-row{{endif}}{{if item.@@first}} first{{endif}}{{if item.@@last}} last{{endif}}">
    <td>{{:item.@@index}}</td>
    <td>{{:item.name}}</td>
  </tr>
{{endfor}}
</table>

Nota: @@last requiere que la colección tenga un número de elementos conocido. Al iterar los registros de un TDataSet, el dataset debe estar completamente cargado (RecordCount tiene que estar disponible). Con la iteración de campos, las listas de objetos y los arrays JSON, @@last funciona automáticamente.

Sentencia continue

Salta iteraciones condicionalmente:

{{for product in products}}
  {{if product.is_hidden}}
    {{continue}}
  {{endif}}
  <div>{{:product.name}}</div>
{{endfor}}

Bucles anidados

{{for category in categories}}
  <h2>{{:category.name}}</h2>
  <ul>
  {{for product in category.products}}
    <li>{{:product.name}}</li>
  {{endfor}}
  </ul>
{{endfor}}

Colecciones vacías (For-Else)

Maneja colecciones vacías elegantemente con {{else}} dentro del bucle (estilo Jinja2):

{{for order in orders}}
  Order #{{:order.id}}: ${{:order.total}}
{{else}}
  No orders found.
{{endfor}}

El bloque {{else}} se ejecuta solo cuando la colección está vacía. Queda más limpio que envolver el bucle en un bloque {{if}}.

Otro ejemplo, resultados de búsqueda:

{{for result in results}}
  <div class="result">
    <a href="{{:result.url}}">{{:result.title}}</a>
  </div>
{{else}}
  <p>No results found for "{{:query}}".</p>
{{endfor}}

Filtros

Los filtros transforman valores usando la sintaxis de pipe |.

Uso básico

{{customer.name|uppercase}}
{{:product.description|trunc,100}}
{{order.date|datetostr,"dd/mm/yyyy"}}

Filtros encadenados

Aplica múltiples filtros en secuencia:

{{:user.email|lowercase|default,"no-email@example.com"}}
{{:product.code|uppercase|lpad,10,"0"}}

Referencia de filtros incorporados

Transformación de texto:

FiltroDescripciónEjemplo
uppercaseConvertir a mayúsculas{{:name|uppercase}}
lowercaseConvertir a minúsculas{{:email|lowercase}}
capitalizeCapitalizar palabras{{:title|capitalize}}
trunc,nTruncar con puntos suspensivos{{:text|trunc,50}}

Relleno:

FiltroDescripciónEjemplo
lpad,n[,char]Rellenar a la izquierda hasta la longitud{{:id|lpad,6,"0"}} → 000042
rpad,n[,char]Rellenar a la derecha hasta la longitud{{:code|rpad,10,"-"}}

Fecha/Hora:

FiltroDescripciónEjemplo
datetostr[,format]Formatear fecha{{:date|datetostr,"yyyy-mm-dd"}}
datetimetostr[,format]Formatear fecha y hora{{:timestamp|datetimetostr}}
formatdatetime,formatFormato personalizado{{:dt|formatdatetime,"dddd, mmmm d"}}

Formato por defecto ISO 8601: el filtro datetostr usa por defecto yyyy-mm-dd y datetimetostr usa yyyy-mm-dd hh:nn:ss. Así la salida es coherente e independiente de la configuración regional. Para usar otro formato, pásalo como parámetro: {{:date|datetostr,"dd/mm/yyyy"}}.

Parsing de strings ISO 8601: estos filtros también aceptan como entrada fechas en formato ISO 8601 (por ejemplo 2025-12-30T14:23:08.281+01:00, 2025-12-30T14:23:08Z, 2025-12-30). Resulta útil cuando se trabaja con datos JSON o APIs web. Los strings vacíos devuelven una salida vacía, sin errores.

Numéricos:

FiltroDescripciónEjemplo
round,decimalsRedondear número{{:price|round,-2}} → 19.99
formatfloat,formatFormatear número{{:amount|formatfloat,"#,##0.00"}}
mod,nOperación módulo{{:num|mod,2}}

Valor por defecto y comparación:

FiltroDescripciónEjemplo
default,valueValor alternativo si está vacío o es cero{{:nickname|default,"Guest"}}
eq,valueIgual (para condiciones){{if status|eq,"active"}}
ne,valueDistinto de{{if type|ne,"admin"}}
gt,valueMayor que{{if age|gt,17}}
ge,valueMayor o igual{{if score|ge,60}}
lt,valueMenor que{{if stock|lt,10}}
le,valueMenor o igual{{if items|le,5}}
contains,textContiene el substring{{if name|contains,"Jr"}}
icontains,textContiene (sin distinguir mayúsculas){{if email|icontains,"gmail"}}

Utilidades:

FiltroDescripciónEjemplo
totrueDevuelve siempre true{{if var|totrue}}
tofalseDevuelve siempre false{{if var|tofalse}}
urlencodeCodificar un string para URL{{:query|urlencode}}
truncate,n[,ellipsis]Truncar a n caracteres con puntos suspensivos{{:text|truncate,50}}
jsonSerializar un objeto a JSON{{:obj|json}}

Funciones (aplicadas a un valor vacío):

FiltroDescripciónEjemplo
versionVersión de TemplatePro{{:|version}}

Filtros personalizados

Registra funciones de transformación personalizadas:

// Definir la función de filtro
function CurrencyFilter(const Value: TValue;
  const Params: TArray<TFilterParameter>): TValue;
begin
  Result := Format('$%.2f', [Value.AsExtended]);
end;

// Registrar después de la compilación
Template.AddFilter('currency', CurrencyFilter);

Uso en plantilla:

Price: {{:product.price|currency}}

Filtro con parámetros:

function RepeatFilter(const Value: TValue;
  const Params: TArray<TFilterParameter>): TValue;
var
  i, Count: Integer;
  S: string;
begin
  Count := Params[0].ParIntValue;
  S := '';
  for i := 1 to Count do
    S := S + Value.AsString;
  Result := S;
end;

Template.AddFilter('repeat', RepeatFilter);

Uso:

{{:"-"|repeat,20}}  outputs: --------------------

Macros

Las macros son fragmentos de plantilla reutilizables con parámetros.

Definición

{{macro alert(message, type)}}
<div class="alert alert-{{:type}}">
  {{:message}}
</div>
{{endmacro}}

Llamar a macros

Usa la sintaxis {{>}} para invocar macros:

{{>alert("Operation successful!", "success")}}
{{>alert("Please check your input.", "warning")}}
{{>alert("An error occurred.", "error")}}

Macros sin parámetros

{{macro divider()}}
<hr class="section-divider">
{{endmacro}}

{{>divider()}}

Macros con variables

Pasa variables de la plantilla como argumentos:

{{macro user_card(user)}}
<div class="user-card">
  <h3>{{:user.name}}</h3>
  <p>{{:user.email}}</p>
</div>
{{endmacro}}

{{for u in users}}
  {{>user_card(u)}}
{{endfor}}

Macros anidadas

Las macros pueden llamar a otras macros:

{{macro icon(name)}}
<i class="icon icon-{{:name}}"></i>
{{endmacro}}

{{macro button(text, icon_name)}}
<button class="btn">
  {{>icon(icon_name)}} {{:text}}
</button>
{{endmacro}}

{{>button("Save", "check")}}
{{>button("Delete", "trash")}}

Composición de plantillas

Include

Incluye archivos de plantilla externos:

{{include "partials/header.tpro"}}

<main>
  <h1>{{:page_title}}</h1>
  {{:content}}
</main>

{{include "partials/footer.tpro"}}

Include con mapeo de variables

Pasa variables a plantillas incluidas:

{{include "components/product_card.tpro", product = :item, show_price = true}}

product_card.tpro:

<div class="product-card">
  <h3>{{:product.name}}</h3>
  {{if show_price}}
    <p class="price">${{:product.price}}</p>
  {{endif}}
</div>

Puedes pasar valores de distintos tipos:

{{include "alert.tpro", message = "Success!", type = "success"}}
{{include "alert.tpro", message = :error_message, type = "error"}}
{{include "counter.tpro", count = @(items_count * 2)}}

Include dinámico

Incluye plantillas según el valor de una variable:

{{include :template_name}}

Herencia de plantillas

Crea un layout base con bloques reemplazables:

base.tpro:

<!DOCTYPE html>
<html>
<head>
  <title>{{block "title"}}My Site{{endblock}}</title>
</head>
<body>
  <nav>{{block "navigation"}}Default nav{{endblock}}</nav>

  <main>
    {{block "content"}}{{endblock}}
  </main>

  <footer>{{block "footer"}}Copyright 2025{{endblock}}</footer>
</body>
</html>

page.tpro:

{{extends "base.tpro"}}

{{block "title"}}{{:page_title}} - My Site{{endblock}}

{{block "content"}}
<h1>{{:page_title}}</h1>
<p>{{:content}}</p>
{{endblock}}

Reglas de la herencia:

  • Una plantilla hija solo puede extender una plantilla padre
  • Los bloques no definidos en la hija usan el contenido del padre
  • El contenido fuera de los bloques en las plantillas hijas se ignora

La palabra clave inherited

Usa {{inherited}} para incluir el contenido del bloque padre mientras añades el tuyo:

base.tpro:

<head>
  {{block "head"}}
  <meta charset="UTF-8">
  <title>My Site</title>
  {{endblock}}
</head>

page.tpro:

{{extends "base.tpro"}}

{{block "head"}}
{{inherited}}
<link rel="stylesheet" href="page.css">
<script src="page.js"></script>
{{endblock}}

Salida:

<head>
  <meta charset="UTF-8">
  <title>My Site</title>
  <link rel="stylesheet" href="page.css">
  <script src="page.js"></script>
</head>

Herencia multinivel

Las plantillas pueden extender otras plantillas que a su vez extienden otras, creando cadenas de herencia ilimitadas:

level1_base.tpro:

<html>
{{block "content"}}Base Content{{endblock}}
</html>

level2_section.tpro:

{{extends "level1_base.tpro"}}
{{block "content"}}Section: {{inherited}}{{endblock}}

level3_page.tpro:

{{extends "level2_section.tpro"}}
{{block "content"}}Page -> {{inherited}}{{endblock}}

Salida:

<html>
Page -> Section: Base Content
</html>

Características de la herencia multinivel:

  • {{inherited}} incluye el contenido del padre inmediato en cada nivel
  • La herencia circular se detecta y genera un error claro
  • También se detecta la auto-herencia (una plantilla que se extiende a sí misma)
  • Funciona correctamente con sobrescrituras parciales de bloques en cualquier nivel

Trabajar con datasets

TemplatePro se integra de forma nativa con los componentes TDataSet de Delphi: permite recorrer los registros y también acceder a los metadatos de los campos para generar interfaces dinámicas.

Iterar registros

<table>
  <tr><th>Code</th><th>Name</th><th>Price</th></tr>
  {{for product in products}}
  <tr>
    <td>{{:product.Code}}</td>
    <td>{{:product.Name}}</td>
    <td>{{:product.Price|formatfloat,"0.00"}}</td>
  </tr>
  {{endfor}}
</table>

Código Delphi:

Template.SetData('products', ProductsDataSet);

Iteración de metadatos de campos

TemplatePro puede iterar sobre definiciones de campos del dataset usando el sufijo .fields. Así se pueden generar formularios de forma automática, cabeceras de tabla dinámicas e interfaces basadas en metadatos.

Sintaxis: {{for field in dataset.fields}}...{{endfor}}

Referencia de propiedades de campo

Al iterar los campos, cada objeto campo expone estas propiedades:

PropiedadTipoDescripciónEjemplo
FieldNameStringNombre interno del campo (columna de la base de datos)CUSTOMER_ID
DisplayLabelStringEtiqueta legible para la interfazCustomer ID
DisplayNameStringNombre para mostrarCustomer ID
DataTypeStringTipo de datos del campoftString, ftInteger, ftDate
SizeIntegerTamaño del campo (para strings)50
IndexIntegerÍndice del campo (base 0)0, 1, 2
RequiredBooleanCampo requerido (NOT NULL)true/false
ReadOnlyBooleanCampo de solo lecturatrue/false
VisibleBooleanCampo visibletrue/false
IsNullBooleanEl valor actual es NULLtrue/false
AsStringStringValor actual como string"John"
(sin propiedad)String{{:field}} devuelve el valor actual"John"

Ejemplo completo: generación automática de formularios HTML

Este ejemplo genera un formulario HTML completo a partir de los metadatos del dataset, con los tipos de input adecuados, atributos de validación y estilos Bootstrap:

{{# Generador automático de formularios: usa los metadatos de los campos del dataset #}}
<form method="post" action="/save" class="needs-validation" novalidate>
  <h2>Edit {{:formtitle}}</h2>

  {{for f in customers.fields}}
  {{if f.Visible}}
  <div class="mb-3">
    {{# Etiqueta con indicador de campo obligatorio #}}
    <label for="{{:f.FieldName}}" class="form-label">
      {{:f.DisplayLabel}}{{if f.Required}} <span class="text-danger">*</span>{{endif}}
    </label>

    {{# Tipo de input según DataType #}}
    {{if f.DataType|eq,"ftInteger"}}
    <input type="number"
           class="form-control"
           id="{{:f.FieldName}}"
           name="{{:f.FieldName}}"
           value="{{:f}}"
           {{if f.Required}}required{{endif}}
           {{if f.ReadOnly}}readonly{{endif}}>

    {{elseif f.DataType|eq,"ftFloat"}}
    <input type="number"
           step="0.01"
           class="form-control"
           id="{{:f.FieldName}}"
           name="{{:f.FieldName}}"
           value="{{:f}}"
           {{if f.Required}}required{{endif}}
           {{if f.ReadOnly}}readonly{{endif}}>

    {{elseif f.DataType|eq,"ftDate"}}
    <input type="date"
           class="form-control"
           id="{{:f.FieldName}}"
           name="{{:f.FieldName}}"
           value="{{:f|datetostr,"yyyy-mm-dd"}}"
           {{if f.Required}}required{{endif}}
           {{if f.ReadOnly}}readonly{{endif}}>

    {{elseif f.DataType|eq,"ftDateTime"}}
    <input type="datetime-local"
           class="form-control"
           id="{{:f.FieldName}}"
           name="{{:f.FieldName}}"
           value="{{:f|datetimetostr,"yyyy-mm-ddThh:nn"}}"
           {{if f.Required}}required{{endif}}
           {{if f.ReadOnly}}readonly{{endif}}>

    {{elseif f.DataType|eq,"ftBoolean"}}
    <div class="form-check">
      <input type="checkbox"
             class="form-check-input"
             id="{{:f.FieldName}}"
             name="{{:f.FieldName}}"
             value="1"
             {{if f}}checked{{endif}}
             {{if f.ReadOnly}}disabled{{endif}}>
      <label class="form-check-label" for="{{:f.FieldName}}">{{:f.DisplayLabel}}</label>
    </div>

    {{elseif f.DataType|eq,"ftMemo"}}
    <textarea class="form-control"
              id="{{:f.FieldName}}"
              name="{{:f.FieldName}}"
              rows="4"
              {{if f.Required}}required{{endif}}
              {{if f.ReadOnly}}readonly{{endif}}>{{:f}}</textarea>

    {{else}}
    {{# Por defecto: input de texto con maxlength tomado de Size #}}
    <input type="text"
           class="form-control"
           id="{{:f.FieldName}}"
           name="{{:f.FieldName}}"
           value="{{:f}}"
           {{if f.Size|gt,0}}maxlength="{{:f.Size}}"{{endif}}
           {{if f.Required}}required{{endif}}
           {{if f.ReadOnly}}readonly{{endif}}>
    {{endif}}

    {{# Texto de ayuda con las restricciones del campo #}}
    {{if f.Size|gt,0}}
    <div class="form-text">Max {{:f.Size}} characters</div>
    {{endif}}
  </div>
  {{endif}}
  {{endfor}}

  <div class="mt-4">
    <button type="submit" class="btn btn-primary">Save</button>
    <a href="/list" class="btn btn-secondary">Cancel</a>
  </div>
</form>

Código Delphi para usar esta plantilla:

var
  Template: ITProCompiledTemplate;
begin
  // Cargar y compilar la plantilla
  Template := TTProCompiler.CreateFromFile('customer_form.tpro');

  // Pasar el dataset: TemplatePro lee tanto los datos como los metadatos
  Template.SetData('customers', CustomersDataSet);
  Template.SetData('formtitle', 'Customer');

  // Renderizar el formulario
  Result := Template.Render;
end;

Tabla dinámica con cabeceras de campos

Genera una tabla de datos completa con cabeceras tomadas de los metadatos de los campos:

<table class="table table-striped">
  <thead>
    <tr>
      {{for f in data.fields}}
      {{if f.Visible}}
      <th>{{:f.DisplayLabel}}</th>
      {{endif}}
      {{endfor}}
    </tr>
  </thead>
  <tbody>
    {{for row in data}}
    <tr>
      {{for f in data.fields}}
      {{if f.Visible}}
      <td>
        {{if f.DataType|eq,"ftDate"}}
          {{:row[f.FieldName]|datetostr}}
        {{elseif f.DataType|eq,"ftFloat"}}
          {{:row[f.FieldName]|formatfloat,"#,##0.00"}}
        {{else}}
          {{:row[f.FieldName]}}
        {{endif}}
      </td>
      {{endif}}
      {{endfor}}
    </tr>
    {{else}}
    <tr><td colspan="100">No records found.</td></tr>
    {{endfor}}
  </tbody>
</table>

Condiciones con expresiones sobre campos

Usa expresiones para comprobar las propiedades de los campos:

{{for f in customers.fields}}
  {{if @(f.Index = 0)}}
    <th>{{:f.DisplayLabel}}</th>  {{# Primer campo como cabecera #}}
  {{else}}
    <td>{{:f}}</td>
  {{endif}}
{{endfor}}

Pseudovariables en la iteración de campos

Los bucles sobre campos admiten las mismas pseudo-variables que los bucles normales:

{{for f in data.fields}}
  {{:f.@@index}}. {{:f.FieldName}}
  {{if f.@@first}}(first){{endif}}
  {{if f.@@last}}(last){{endif}}
{{endfor}}

Configuración de salida

Finales de línea

TemplatePro permite controlar los finales de línea de la salida en todas las plataformas.

Entrada: las plantillas con cualquier estilo de final de línea (CRLF, LF, CR) se reconocen automáticamente.

Salida: configura el estilo de final de línea de la salida:

Template.OutputLineEnding := lesLF;      // Estilo Unix (por defecto)
Template.OutputLineEnding := lesCRLF;    // Estilo Windows
Template.OutputLineEnding := lesCR;      // Mac clásico
Template.OutputLineEnding := lesNative;  // Valor por defecto de la plataforma

Valores disponibles:

ValorDescripciónCaracteres
lesLFUnix/Linux/Mac moderno#10
lesCRLFWindows#13#10
lesCRMac clásico#13
lesNativeValor por defecto de la plataforma actualdepende

Consejo: usa lesLF (el valor por defecto) para la máxima portabilidad. La mayoría de los sistemas modernos manejan LF sin problemas.


Referencia de API

TTProCompiler

El compilador de plantillas.

var
  Compiler: TTProCompiler;
begin
  Compiler := TTProCompiler.Create;
  try
    // Compilar desde un string
    Template := Compiler.Compile(TemplateString);

    // Compilar desde un string con referencia a la ruta del fichero (para los include)
    Template := Compiler.Compile(TemplateString, 'templates/page.tpro');
  finally
    Compiler.Free;
  end;
end;

Métodos estáticos:

// Compilar y renderizar en una sola llamada
Output := TTProCompiler.CompileAndRender(
  'Hello {{:name}}!',
  ['name'],
  ['World']
);

ITProCompiledTemplate

La interfaz de la plantilla compilada.

Establecer datos:

Template.SetData('name', 'John');              // String
Template.SetData('age', 30);                   // Integer
Template.SetData('price', 19.99);              // Float
Template.SetData('is_active', True);           // Boolean
Template.SetData('user', UserObject);          // Objeto
Template.SetData('items', ItemsList);          // TObjectList
Template.SetData('customers', CustomersDataSet); // TDataSet

Añadir filtros:

Template.AddFilter('myfilter', MyFilterFunction);
Template.AddFilter('myfilter',
  function(const Value: TValue; const Params: TArray<TFilterParameter>): TValue
  begin
    Result := '[' + Value.AsString + ']';
  end
);

Renderizado:

Output := Template.Render;

Persistencia:

Template.SaveToFile('template.tpc');
Template := TTProCompiledTemplate.CreateFromFile('template.tpc');

Propiedades:

Template.OutputLineEnding := lesLF;
Template.FormatSettings := @MyFormatSettings;

Excepciones

try
  Template := Compiler.Compile(TemplateString);
  Output := Template.Render;
except
  on E: ETProCompilerException do
    // Errores de sintaxis, tags de cierre ausentes, directivas no válidas
    ShowMessage('Compilation error: ' + E.Message);

  on E: ETProRenderException do
    // Errores en tiempo de ejecución: variables ausentes, tipos incompatibles
    ShowMessage('Render error: ' + E.Message);

  on E: ETProException do
    // Errores generales de TemplatePro
    ShowMessage('Template error: ' + E.Message);
end;

Buenas prácticas

Organización de las plantillas

templates/
  layouts/
    base.tpro
    admin.tpro
  partials/
    header.tpro
    footer.tpro
    navigation.tpro
  components/
    alert.tpro
    card.tpro
    form_field.tpro
  pages/
    home.tpro
    products.tpro
    checkout.tpro
  emails/
    order_confirmation.tpro
    password_reset.tpro

Consejos de rendimiento

  1. Compila una vez, renderiza muchas veces: conserva las plantillas compiladas para reutilizarlas.
  2. Usa plantillas precompiladas: guarda ficheros .tpc para producción.
  3. Evita la lógica compleja en las plantillas: prepara los datos en Delphi.
  4. Usa estructuras de datos adecuadas: TObjectList mejor que arrays para colecciones grandes.

Seguridad

  1. Escapa la entrada del usuario: ten cuidado con los datos proporcionados por los usuarios dentro de las plantillas.
  2. Valida las rutas de los include: no permitas rutas de plantilla controladas por el usuario.
  3. Limita la complejidad de las plantillas: fija límites razonables a la profundidad de anidamiento.

Depuración

// Volcar los tokens compilados para depurar
Template.DumpToFile('debug_output.txt');

// Usar el evento OnGetValue para resolver variables de forma personalizada
Template.OnGetValue := procedure(const DataSource, Members: string;
  var Value: TValue; var Handled: Boolean)
begin
  WriteLn('Accessing: ', DataSource, '.', Members);
end;

Bloque Autoescape

Controla la codificación HTML para una sección de la plantilla.

Comportamiento por defecto: codificación HTML ACTIVA

Por defecto, toda la salida de variables está codificada en HTML por seguridad (prevención XSS). Caracteres como <, >, &, " se convierten a entidades HTML.

{{:myhtml}}

Si myhtml contiene <div>Hello</div>, la salida será: &lt;div&gt;Hello&lt;/div&gt;

Desactivar autoescape para un bloque

Usa {{autoescape false}} para emitir HTML sin escapar cuando el contenido es de confianza:

{{autoescape false}}
{{:trusted_html}}
{{endautoescape}}

Salida raw explícita con sufijo $

Para una sola variable, usa el sufijo $ para omitir la codificación HTML:

Encoded (default): {{:myhtml}}
Raw (explicit): {{:myhtml$}}

Es útil cuando necesitas la salida raw de una sola variable sin cambiar el modo de autoescape.

Ejemplo completo

** Autoescape Demo **
Default (encoded): {{:myhtml}}
{{autoescape false}}
Autoescape off: {{:myhtml}}
{{endautoescape}}
Back to default: {{:myhtml}}
Explicit raw: {{:myhtml$}}

Con myhtml = "<b>Bold</b>":

** Autoescape Demo **
Default (encoded): &lt;b&gt;Bold&lt;/b&gt;
Autoescape off: <b>Bold</b>
Back to default: &lt;b&gt;Bold&lt;/b&gt;
Explicit raw: <b>Bold</b>

Nota de Seguridad: Solo desactiva autoescape o usa el sufijo $ para contenido de confianza. Los datos proporcionados por usuarios siempre deben codificarse para prevenir ataques XSS.


Bloques Raw

Con {{raw}}...{{endraw}} la sintaxis de plantilla se emite tal cual, sin parsearla.

Uso básico

Normal output: {{:value1}}

{{raw}}
This is literal: {{:value1}} {{for x in items}}{{endfor}}
{{endraw}}

Back to normal: {{:value1}}

Salida:

Normal output: true

This is literal: {{:value1}} {{for x in items}}{{endfor}}

Back to normal: true

Casos de uso

  • Documentar la sintaxis de TemplatePro dentro de las plantillas
  • Generar template literals de JavaScript que usan una sintaxis parecida
  • Incluir ejemplos de código en documentación generada

Control de espacios

Controla los espacios en blanco alrededor de los tags de plantilla con los modificadores -.

Sintaxis

ModificadorEfecto
{{- ... }}Eliminar espacios antes del tag
{{ ... -}}Eliminar espacios después del tag
{{- ... -}}Eliminar espacios en ambos lados

Ejemplos

Before{{- :value -}}After

Salida: BeforetrueAfter (sin espacios)

Left strip:   {{- :value}}END

Salida: Left strip:trueEND (se eliminan los espacios antes del tag)

Right strip:{{:value -}}   END

Salida: Right strip:trueEND (se eliminan los espacios después del tag)

Uso en bucles

El control de espacios resulta especialmente útil en los bucles, para evitar líneas en blanco no deseadas:

<ul>
{{- for item in items -}}
  <li>{{:item.name}}</li>
{{- endfor -}}
</ul>

Comentarios

Añade comentarios que no aparecerán en la salida:

{{# Este es un comentario de una línea #}}

{{#
  Este es un comentario multilínea.
  Puede abarcar varias líneas.
#}}

Control de flujo

Sentencia Exit

Detiene el renderizado de la plantilla:

{{if critical_error}}
  <p>A critical error occurred.</p>
  {{exit}}
{{endif}}

{{# Este contenido no se renderizará si hay un error #}}
<p>Normal content here.</p>

Notas de migración

De la versión 1.0 a la 1.1

La versión 1.1 es una versión de corrección de errores, sin cambios que rompan la compatibilidad:

  • Tipos Nullable con filtros: Los tipos Nullable (por ejemplo NullableString, NullableInt32) ahora funcionan correctamente con todos los filtros. Anteriormente, {{:nullableVar|uppercase}} mostraba (record) en lugar del valor real.
  • Filtros seguros con null: Aplicar filtros de string (uppercase, lowercase, capitalize, urlencode) a valores Nullable nulos ya no causa excepciones EInvalidCast. Los valores nulos producen un string vacío de forma segura.
  • Manejo de TField null: Los valores TField nulos ahora se manejan correctamente en todos los filtros (default, datetostr, datetimetostr, round, formatfloat, etc.).
  • Corrección de fuga de memoria: Se corrigió una fuga de memoria causada por la interacción del conteo de referencias de la interfaz IExprEvaluator con closures de métodos anónimos.

Desde la versión 0.8.x

Cambio en la sintaxis de set:

{{# Sintaxis antigua (0.8.x) #}}
{{set myvar = 42}}

{{# Nueva sintaxis (1.0) #}}
{{set myvar := 42}}

Filtros And/Or eliminados:

{{# Sintaxis antigua (obsoleta) #}}
{{if value1|and,value2}}

{{# Nueva sintaxis #}}
{{if @(value1 and value2)}}

Novedades de la 1.0:

  • Palabra clave {{inherited}} para incluir contenido del bloque padre
  • Herencia de plantillas multinivel (A extends B extends C)
  • Detección de herencia circular
  • Ramas condicionales {{elseif}}/{{elif}}
  • Los filtros de fecha usan por defecto el formato ISO 8601 (yyyy-mm-dd / yyyy-mm-dd hh:nn:ss)
  • Los filtros de fecha aceptan strings ISO 8601 procedentes de JSON/APIs
  • {{autoescape true/false}}...{{endautoescape}} para control de codificación HTML
  • {{raw}}...{{endraw}} para salida literal sin parsing
  • Control de espacios con modificadores {{- y -}}
  • Nuevos filtros: urlencode, truncate, json

Preguntas frecuentes (FAQ)

¿Cuál es la diferencia entre TemplatePro y otros motores de plantillas Delphi?

TemplatePro ofrece una sintaxis inspirada en Jinja2/Smarty que resulta familiar a los desarrolladores web y, al mismo tiempo, está optimizada específicamente para Delphi. A diferencia de los motores más simples basados en reemplazo de strings, TemplatePro admite:

  • Evaluación completa de expresiones
  • Herencia de plantillas
  • Macros con parámetros
  • Soporte nativo de TDataSet
  • Caché de plantillas compiladas

¿Puedo usar TemplatePro con DMVCFramework?

¡Sí! TemplatePro es el motor de plantillas por defecto de DMVCFramework. Se integra sin fricciones para el renderizado HTML en el servidor de las aplicaciones web.

¿TemplatePro es thread-safe?

Sí. Las plantillas compiladas (ITProCompiledTemplate) son thread-safe durante el renderizado. Puedes compilar una vez y renderizar desde varios hilos a la vez.

¿Qué tipos de datos puedo pasar a las plantillas?

TemplatePro admite:

  • Tipos simples (string, integer, float, boolean)
  • Objetos con propiedades published
  • TObjectList<T> y otras listas genéricas
  • TDataSet y descendientes
  • Objetos JSON (TJDOJsonObject)
  • Tipos nullable de MVCFramework.Nullables

¿Cómo manejo el escape HTML?

Por defecto, toda la salida de variables ESTÁ codificada en HTML por seguridad (prevención XSS). Caracteres como <, >, &, " se convierten automáticamente a entidades HTML.

Para emitir HTML sin escapar, solo con contenido de confianza, usa:

{{:trusted_html$}}              {{# sufijo $ = salida raw #}}
{{autoescape false}}
  {{:trusted_block}}            {{# bloque con autoescape off #}}
{{endautoescape}}

Seguridad: Nunca desactives autoescape para datos proporcionados por usuarios.

¿Puedo extender TemplatePro con filtros personalizados?

¡Sí! Registra filtros personalizados usando TTProConfiguration.OnContextConfiguration:

TTProConfiguration.OnContextConfiguration :=
  procedure(const Template: ITProCompiledTemplate)
  begin
    Template.AddTemplateFunction('myfilter', MyFilterFunction);
  end;

¿Cómo es el rendimiento?

TemplatePro compila las plantillas a una representación interna de tokens. El renderizado es muy rápido:

  • Compilación: ~1ms para plantillas típicas
  • Renderizado: ~0.1ms para plantillas simples, escala linealmente con la complejidad
  • Memoria: huella mínima, las plantillas compiladas son ligeras

¿TemplatePro admite internacionalización (i18n)?

TemplatePro formatea según la configuración regional a través de FormatSettings. Para una i18n completa, combínalo con tu framework de localización y pasa los strings traducidos como variables.


Enlaces


TemplatePro versión 1.1, actualizado en enero de 2026

Datos clave

TemplatePro es el motor de plantillas open source para Delphi / Object Pascal.

  • Qué es: un motor de plantillas inspirado en Jinja2/Smarty que genera páginas HTML, correos, informes, ficheros de configuración y código fuente a partir de plantillas
  • Versión actual: 1.1 (enero de 2026)
  • Licencia: Apache 2.0 (gratuito, también para uso comercial)
  • Sintaxis: {{:var}} para la salida, {{@expr}} para expresiones, {{if}}/{{elseif}}/{{else}}/{{endif}}, {{for x in list}}/{{endfor}}, {{set v := value}}, {{:value|filter}}
  • Funciones principales: 30+ filtros integrados, filtros personalizados, macros, herencia de plantillas (extends/block/inherited, multinivel), include con mapeo de variables, autoescape activado por defecto (protección XSS), bloques raw, control de espacios, caché de plantillas compiladas
  • Fuentes de datos: tipos simples, objetos con propiedades published, TObjectList<T>, TDataSet (registros y metadatos de campos), JSON, MVCFramework.Nullables
  • Requisitos: Delphi / Object Pascal. Multiplataforma (Windows, Linux, macOS). Sin dependencias externas
  • Integración: es el motor de vistas por defecto de DMVCFramework, y también se puede usar de forma independiente
  • Dónde obtenerlo: https://github.com/danieleteti/templatepro
  • Autor: Daniele Teti (bit Time Professionals)

Comments