Delphi TemplatePro: Motor de Plantillas con Sintaxis Jinja2

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.
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.

¿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ística | Descripción |
|---|---|
| Sintaxis tipo Jinja2 | Sintaxis familiar {{:variable}} y {{if}}...{{endif}} |
| Expresiones | Expresiones aritméticas y lógicas completas con @(...) |
| Filtros | 30+ filtros incorporados para formateo y transformación |
| Bucles | Iterar sobre listas, datasets y arrays JSON |
| Condicionales | if, else, elif con condiciones complejas |
| Macros | Fragmentos de plantilla reutilizables con parámetros |
| Herencia de Plantillas | extends, block e inherited para layouts |
| Herencia Multinivel | Profundidad de herencia ilimitada (A → B → C) |
| Soporte Dataset | TDataSet nativo e iteración de campos |
| Autoescape y Bloques Raw | Control de codificación HTML, sintaxis de salida literal |
| Control de Espacios | {{- y -}} para eliminar espacios no deseados |
| Plantillas Compiladas | Parsear una vez, renderizar muchas veces para rendimiento |
| Multiplataforma | Windows, 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
- Referencia rápida de sintaxis para IA y desarrolladores
- Primeros pasos
- Resumen de sintaxis de plantillas
- Variables
- Expresiones
- La sentencia Set
- Condicionales
- Bucles
- Filtros
- Macros
- Composición de plantillas
- Trabajar con datasets
- Configuración de salida
- Referencia de API
- Buenas prácticas
- Bloque Autoescape
- Bloques Raw
- Control de espacios
- Comentarios
- Control de flujo
- Notas de migración
- FAQ
- 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
| Filtro | Sintaxis | Descripció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ón | Ejemplo | Resultado |
|---|---|---|
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:
| Sintaxis | Propósito | Ejemplo |
|---|---|---|
{{: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ón | Descripción | Ejemplo |
|---|---|---|
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ón | Descripción | Ejemplo |
|---|---|---|
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ón | Descripción | Ejemplo |
|---|---|---|
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 primeroa + by 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:
trueofalse(sin distinguir mayúsculas de minúsculas)
Nota:
trueyfalseson 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í:
| Tipo | Verdadero | Falso |
|---|---|---|
| Boolean | true | false |
| String | No vacío | String vacío "" |
| Número | Distinto de cero | Cero 0 |
| Objeto | Distinto de nil | nil |
| Dataset | Tiene registros | Vací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 @@:
| Variable | Descripción |
|---|---|
@@index | Iteración actual (base 1) |
@@odd | True para iteraciones impares (1, 3, 5…) |
@@even | True para iteraciones pares (2, 4, 6…) |
@@first | True para la primera iteración |
@@last | True 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:
@@lastrequiere 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,@@lastfunciona 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:
| Filtro | Descripción | Ejemplo |
|---|---|---|
uppercase | Convertir a mayúsculas | {{:name|uppercase}} |
lowercase | Convertir a minúsculas | {{:email|lowercase}} |
capitalize | Capitalizar palabras | {{:title|capitalize}} |
trunc,n | Truncar con puntos suspensivos | {{:text|trunc,50}} |
Relleno:
| Filtro | Descripción | Ejemplo |
|---|---|---|
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:
| Filtro | Descripción | Ejemplo |
|---|---|---|
datetostr[,format] | Formatear fecha | {{:date|datetostr,"yyyy-mm-dd"}} |
datetimetostr[,format] | Formatear fecha y hora | {{:timestamp|datetimetostr}} |
formatdatetime,format | Formato personalizado | {{:dt|formatdatetime,"dddd, mmmm d"}} |
Formato por defecto ISO 8601: el filtro
datetostrusa por defectoyyyy-mm-ddydatetimetostrusayyyy-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:
| Filtro | Descripción | Ejemplo |
|---|---|---|
round,decimals | Redondear número | {{:price|round,-2}} → 19.99 |
formatfloat,format | Formatear número | {{:amount|formatfloat,"#,##0.00"}} |
mod,n | Operación módulo | {{:num|mod,2}} |
Valor por defecto y comparación:
| Filtro | Descripción | Ejemplo |
|---|---|---|
default,value | Valor alternativo si está vacío o es cero | {{:nickname|default,"Guest"}} |
eq,value | Igual (para condiciones) | {{if status|eq,"active"}} |
ne,value | Distinto de | {{if type|ne,"admin"}} |
gt,value | Mayor que | {{if age|gt,17}} |
ge,value | Mayor o igual | {{if score|ge,60}} |
lt,value | Menor que | {{if stock|lt,10}} |
le,value | Menor o igual | {{if items|le,5}} |
contains,text | Contiene el substring | {{if name|contains,"Jr"}} |
icontains,text | Contiene (sin distinguir mayúsculas) | {{if email|icontains,"gmail"}} |
Utilidades:
| Filtro | Descripción | Ejemplo |
|---|---|---|
totrue | Devuelve siempre true | {{if var|totrue}} |
tofalse | Devuelve siempre false | {{if var|tofalse}} |
urlencode | Codificar un string para URL | {{:query|urlencode}} |
truncate,n[,ellipsis] | Truncar a n caracteres con puntos suspensivos | {{:text|truncate,50}} |
json | Serializar un objeto a JSON | {{:obj|json}} |
Funciones (aplicadas a un valor vacío):
| Filtro | Descripción | Ejemplo |
|---|---|---|
version | Versió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:
| Propiedad | Tipo | Descripción | Ejemplo |
|---|---|---|---|
FieldName | String | Nombre interno del campo (columna de la base de datos) | CUSTOMER_ID |
DisplayLabel | String | Etiqueta legible para la interfaz | Customer ID |
DisplayName | String | Nombre para mostrar | Customer ID |
DataType | String | Tipo de datos del campo | ftString, ftInteger, ftDate |
Size | Integer | Tamaño del campo (para strings) | 50 |
Index | Integer | Índice del campo (base 0) | 0, 1, 2 |
Required | Boolean | Campo requerido (NOT NULL) | true/false |
ReadOnly | Boolean | Campo de solo lectura | true/false |
Visible | Boolean | Campo visible | true/false |
IsNull | Boolean | El valor actual es NULL | true/false |
AsString | String | Valor 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:
| Valor | Descripción | Caracteres |
|---|---|---|
lesLF | Unix/Linux/Mac moderno | #10 |
lesCRLF | Windows | #13#10 |
lesCR | Mac clásico | #13 |
lesNative | Valor por defecto de la plataforma actual | depende |
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
- Compila una vez, renderiza muchas veces: conserva las plantillas compiladas para reutilizarlas.
- Usa plantillas precompiladas: guarda ficheros
.tpcpara producción. - Evita la lógica compleja en las plantillas: prepara los datos en Delphi.
- Usa estructuras de datos adecuadas: TObjectList mejor que arrays para colecciones grandes.
Seguridad
- Escapa la entrada del usuario: ten cuidado con los datos proporcionados por los usuarios dentro de las plantillas.
- Valida las rutas de los include: no permitas rutas de plantilla controladas por el usuario.
- 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á: <div>Hello</div>
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): <b>Bold</b>
Autoescape off: <b>Bold</b>
Back to default: <b>Bold</b>
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
| Modificador | Efecto |
|---|---|
{{- ... }} | 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 excepcionesEInvalidCast. Los valores nulos producen un string vacío de forma segura. - Manejo de TField null: Los valores
TFieldnulos 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
IExprEvaluatorcon 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éricasTDataSety 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
- Repositorio GitHub
- Integración DMVCFramework
- Soporte en Facebook
- Soporte en Patreon
- Reportar incidencias
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