2026-01-26 05:12:14 +01:00
{# retoor < retoor @ molodetz . nl > #}
{% extends 'page.html' %}
2026-01-24 23:40:22 +01:00
2026-01-26 05:12:14 +01:00
{% set page_title = "Template Rendering" %}
{% set breadcrumb = [{"url": "tutorials/index.html", "title": "Tutorials"}, {"title": "Template Rendering"}] %}
2026-01-26 10:21:03 +01:00
{% set prev_page = {"url": "tutorials/dataset-orm.html", "title": "Dataset ORM"} %}
2026-01-26 05:12:14 +01:00
{% set next_page = {"url": "tutorials/cli-tool.html", "title": "CLI Tool"} %}
{% block article %}
{% raw %}
2026-01-24 23:40:22 +01:00
< h1 > Template Rendering</ h1 >
< p > In this tutorial, you will learn to use Jinja templates to generate HTML pages, reports, and configuration files. Jinja is a powerful templating engine that separates your presentation logic from your data.</ p >
< h2 > What You Will Learn</ h2 >
< ul >
< li > Basic template syntax (variables, expressions)</ li >
< li > Control structures (if, for)</ li >
< li > Template inheritance</ li >
< li > Filters and macros</ li >
< li > Loading templates from files</ li >
</ ul >
< h2 > Step 1: Basic Templates</ h2 >
< p > Create a file called < code > template_demo.wren</ code > :</ p >
< pre >< code > import "jinja" for Environment, DictLoader
var env = Environment.new(DictLoader.new({
"greeting": "Hello, {{ name }}!"
}))
var template = env.getTemplate("greeting")
var result = template.render({"name": "World"})
System.print(result) // Hello, World!</ code ></ pre >
< h2 > Step 2: Variables and Expressions</ h2 >
< p > Jinja supports various expressions inside < code > {{ }}</ code > :</ p >
< pre >< code > import "jinja" for Environment, DictLoader
var templates = {
"expressions": "
Name: {{ user.name }}
Age: {{ user.age }}
Adult: {{ user.age >= 18 }}
Items: {{ items | length }}
First: {{ items[0] }}
Upper: {{ user.name | upper }}
"
}
var env = Environment.new(DictLoader.new(templates))
var template = env.getTemplate("expressions")
var result = template.render({
"user": {"name": "Alice", "age": 25},
"items": ["apple", "banana", "cherry"]
})
System.print(result)</ code ></ pre >
< p > Output:</ p >
< pre >< code > Name: Alice
Age: 25
Adult: true
Items: 3
First: apple
Upper: ALICE</ code ></ pre >
< h2 > Step 3: Control Structures</ h2 >
< h3 > Conditionals</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var templates = {
"status": "
{% if user.active %}
User {{ user.name }} is active.
{% elif user.pending %}
User {{ user.name }} is pending approval.
{% else %}
User {{ user.name }} is inactive.
{% endif %}
"
}
var env = Environment.new(DictLoader.new(templates))
var template = env.getTemplate("status")
System.print(template.render({"user": {"name": "Bob", "active": true}}))
System.print(template.render({"user": {"name": "Carol", "pending": true}}))
System.print(template.render({"user": {"name": "Dave", "active": false}}))</ code ></ pre >
< h3 > Loops</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var templates = {
"list": "
Shopping List:
{% for item in items %}
- {{ item.name }}: ${{ item.price }}
{% endfor %}
Total items: {{ items | length }}
"
}
var env = Environment.new(DictLoader.new(templates))
var template = env.getTemplate("list")
var result = template.render({
"items": [
{"name": "Apples", "price": 2.99},
{"name": "Bread", "price": 3.50},
{"name": "Milk", "price": 4.25}
]
})
System.print(result)</ code ></ pre >
< h3 > Loop Variables</ h3 >
< pre >< code > import "jinja" for Environment, DictLoader
var templates = {
"numbered": "
{% for item in items %}
{{ loop.index }}. {{ item }}{% if loop.first %} (first){% endif %}{% if loop.last %} (last){% endif %}
{% endfor %}
"
}
var env = Environment.new(DictLoader.new(templates))
var template = env.getTemplate("numbered")
var result = template.render({
"items": ["Red", "Green", "Blue"]
})
System.print(result)</ code ></ pre >
< p > Output:</ p >
< pre >< code > 1. Red (first)
2. Green
3. Blue (last)</ code ></ pre >
< h2 > Step 4: Filters</ h2 >
< p > Filters transform values using the pipe (< code > |</ code > ) syntax:</ p >
< pre >< code > import "jinja" for Environment, DictLoader
var templates = {
"filters": "
{{ name | upper }}
{{ name | lower }}
{{ name | capitalize }}
{{ name | title }}
{{ price | round(2) }}
{{ items | join(', ') }}
{{ text | truncate(20) }}
{{ html | escape }}
{{ value | default('N/A') }}
"
}
var env = Environment.new(DictLoader.new(templates))
var template = env.getTemplate("filters")
var result = template.render({
"name": "hELLo WoRLD",
"price": 19.99567,
"items": ["a", "b", "c"],
"text": "This is a very long string that should be truncated",
2026-01-26 05:12:14 +01:00
"html": "< script> alert('xss')< /script> ",
2026-01-24 23:40:22 +01:00
"value": null
})
System.print(result)</ code ></ pre >
< h2 > Step 5: Template Inheritance</ h2 >
< p > Template inheritance allows you to build a base template with common structure:</ p >
< pre >< code > import "jinja" for Environment, DictLoader
var templates = {
"base.html": "
<!DOCTYPE html>
2026-01-26 05:12:14 +01:00
< html >
< head >
2026-01-24 23:40:22 +01:00
< title > {% block title %}My Site{% endblock %}</ title >
</ head >
< body >
< header >
< nav > Home | About | Contact</ nav >
</ header >
< main >
{% block content %}{% endblock %}
</ main >
< footer >
Copyright 2024
</ footer >
</ body >
</ html >
",
"home.html": "
{% extends 'base.html' %}
{% block title %}Home - My Site{% endblock %}
{% block content %}
2026-01-26 05:12:14 +01:00
< h1 > Welcome!</ h1 >
2026-01-24 23:40:22 +01:00
< p > This is the home page.</ p >
{% endblock %}
",
"about.html": "
{% extends 'base.html' %}
{% block title %}About - My Site{% endblock %}
{% block content %}
2026-01-26 05:12:14 +01:00
< h1 > About Us</ h1 >
2026-01-24 23:40:22 +01:00
< p > {{ description }}</ p >
{% endblock %}
"
}
var env = Environment.new(DictLoader.new(templates))
System.print("=== Home Page ===")
System.print(env.getTemplate("home.html").render({}))
System.print("\n=== About Page ===")
System.print(env.getTemplate("about.html").render({
"description": "We are a software company."
}))</ code ></ pre >
< h2 > Step 6: Macros</ h2 >
< p > Macros are reusable template functions:</ p >
< pre >< code > import "jinja" for Environment, DictLoader
var templates = {
"forms": "
{% macro input(name, type='text', value='', placeholder='') %}
< input type = \"{{ type }}\" name = \"{{ name }}\" value = \"{{ value }}\" placeholder = \"{{ placeholder }}\" >
{% endmacro %}
{% macro button(text, type='button', class='btn') %}
< button type = \"{{ type }}\" class = \"{{ class }}\" > {{ text }}</ button >
{% endmacro %}
< form >
{{ input('username', placeholder='Enter username') }}
{{ input('password', type='password', placeholder='Enter password') }}
{{ button('Login', type='submit', class='btn btn-primary') }}
</ form >
"
}
var env = Environment.new(DictLoader.new(templates))
var result = env.getTemplate("forms").render({})
System.print(result)</ code ></ pre >
< h2 > Step 7: Loading Templates from Files</ h2 >
< p > For larger projects, store templates in files:</ p >
< pre >< code > import "jinja" for Environment, FileSystemLoader
import "io" for File
File.write("templates/base.html", "
<!DOCTYPE html>
< html >
< head >
< title > {% block title %}{% endblock %}</ title >
</ head >
< body >
{% block content %}{% endblock %}
</ body >
</ html >
")
File.write("templates/page.html", "
{% extends 'base.html' %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
2026-01-26 05:12:14 +01:00
< h1 > {{ title }}</ h1 >
2026-01-24 23:40:22 +01:00
{{ content }}
{% endblock %}
")
var env = Environment.new(FileSystemLoader.new("templates"))
var template = env.getTemplate("page.html")
var html = template.render({
"title": "My Page",
"content": "< p > Hello from a file-based template!</ p > "
})
System.print(html)</ code ></ pre >
< h2 > Step 8: Building a Report Generator</ h2 >
< p > Let's build a practical example - generating HTML reports:</ p >
< pre >< code > import "jinja" for Environment, DictLoader
import "io" for File
import "sqlite" for Sqlite
import "datetime" for DateTime
class ReportGenerator {
construct new() {
_env = Environment.new(DictLoader.new({
"report": "
<!DOCTYPE html>
< html >
< head >
< title > {{ title }}</ title >
< style >
body { font-family : Arial , sans-serif ; margin : 40 px ; }
h1 { color : #333 ; }
table { border-collapse : collapse ; width : 100 \ % ; }
th , td { border : 1 px solid #ddd ; padding : 8 px ; text-align : left ; }
th { background-color : #4CAF50 ; color : white ; }
tr : nth-child ( even ) { background-color : #f2f2f2 ; }
. summary { background : #f9f9f9 ; padding : 20 px ; margin : 20 px 0 ; }
. footer { margin-top : 40 px ; color : #666 ; font-size : 12 px ; }
</ style >
</ head >
< body >
< h1 > {{ title }}</ h1 >
< p > Generated: {{ generated_at }}</ p >
< div class = \"summary\" >
< h2 > Summary</ h2 >
< p > Total Records: {{ data | length }}</ p >
{% if total %}
< p > Total Amount: ${{ total | round(2) }}</ p >
{% endif %}
</ div >
< h2 > Details</ h2 >
< table >
< thead >
< tr >
{% for header in headers %}
< th > {{ header }}</ th >
{% endfor %}
</ tr >
</ thead >
< tbody >
{% for row in data %}
< tr >
{% for header in headers %}
< td > {{ row[header] }}</ td >
{% endfor %}
</ tr >
{% endfor %}
</ tbody >
</ table >
< div class = \"footer\" >
Report generated by Wren-CLI Report Generator
</ div >
</ body >
</ html >
"
}))
}
generate(title, headers, data, options) {
var template = _env.getTemplate("report")
var total = null
if (options && options["sumColumn"]) {
total = 0
for (row in data) {
total = total + (row[options["sumColumn"]] || 0)
}
}
return template.render({
"title": title,
"headers": headers,
"data": data,
"total": total,
"generated_at": DateTime.now().toString
})
}
save(filename, html) {
File.write(filename, html)
System.print("Report saved to %(filename)")
}
}
var generator = ReportGenerator.new()
var salesData = [
{"Product": "Widget A", "Quantity": 150, "Price": 29.99, "Total": 4498.50},
{"Product": "Widget B", "Quantity": 75, "Price": 49.99, "Total": 3749.25},
{"Product": "Widget C", "Quantity": 200, "Price": 19.99, "Total": 3998.00},
{"Product": "Widget D", "Quantity": 50, "Price": 99.99, "Total": 4999.50}
]
var html = generator.generate(
"Sales Report Q4 2024",
["Product", "Quantity", "Price", "Total"],
salesData,
{"sumColumn": "Total"}
)
generator.save("sales_report.html", html)
System.print("Report generated successfully!")</ code ></ pre >
< h2 > Step 9: Email Templates</ h2 >
< p > Create personalized emails with templates:</ p >
< pre >< code > import "jinja" for Environment, DictLoader
class EmailGenerator {
construct new() {
_env = Environment.new(DictLoader.new({
"welcome": "
Subject: Welcome to {{ company }}, {{ user.name }}!
Dear {{ user.name }},
Thank you for joining {{ company }}! We are excited to have you.
Your account details:
- Username: {{ user.username }}
- Email: {{ user.email }}
- Plan: {{ user.plan | default('Free') }}
{% if user.plan == 'Premium' %}
As a Premium member, you have access to:
{% for feature in premium_features %}
- {{ feature }}
{% endfor %}
{% endif %}
If you have any questions, please contact us at {{ support_email }}.
Best regards,
The {{ company }} Team
",
"order_confirmation": "
Subject: Order #{{ order.id }} Confirmed
Dear {{ customer.name }},
Thank you for your order!
Order Details:
{% for item in order.items %}
- {{ item.name }} x {{ item.quantity }} @ ${{ item.price }} = ${{ item.total }}
{% endfor %}
Subtotal: ${{ order.subtotal | round(2) }}
Tax: ${{ order.tax | round(2) }}
Total: ${{ order.total | round(2) }}
Shipping to:
{{ customer.address.street }}
{{ customer.address.city }}, {{ customer.address.state }} {{ customer.address.zip }}
Estimated delivery: {{ delivery_date }}
Thank you for shopping with us!
"
}))
}
welcome(user, company) {
return _env.getTemplate("welcome").render({
"user": user,
"company": company,
"support_email": "support@%(company.lower).com",
"premium_features": [
"Unlimited storage",
"Priority support",
"Advanced analytics",
"Custom integrations"
]
})
}
orderConfirmation(customer, order) {
return _env.getTemplate("order_confirmation").render({
"customer": customer,
"order": order,
"delivery_date": "3-5 business days"
})
}
}
var emails = EmailGenerator.new()
var welcomeEmail = emails.welcome(
{
"name": "Alice Smith",
"username": "alice",
"email": "alice@example.com",
"plan": "Premium"
},
"Acme Corp"
)
System.print(welcomeEmail)
System.print("\n---\n")
var orderEmail = emails.orderConfirmation(
{
"name": "Bob Jones",
"address": {
"street": "123 Main St",
"city": "Springfield",
"state": "IL",
"zip": "62701"
}
},
{
"id": "ORD-12345",
"items": [
{"name": "Blue Widget", "quantity": 2, "price": 29.99, "total": 59.98},
{"name": "Red Gadget", "quantity": 1, "price": 49.99, "total": 49.99}
],
"subtotal": 109.97,
"tax": 9.90,
"total": 119.87
}
)
System.print(orderEmail)</ code ></ pre >
< div class = "admonition tip" >
< div class = "admonition-title" > Tip</ div >
< p > Use the < code > escape</ code > filter on user-provided content to prevent XSS attacks in HTML output: < code > {{ user_input | escape }}</ code ></ p >
</ div >
< div class = "admonition note" >
< div class = "admonition-title" > Note</ div >
< p > Jinja whitespace can be controlled with < code > {%-</ code > and < code > -%}</ code > to strip whitespace before or after tags.</ p >
</ div >
< h2 > Next Steps</ h2 >
< ul >
< li > Explore all < a href = "../api/jinja.html" > Jinja filters and features</ a ></ li >
< li > Combine with < a href = "database-app.html" > database queries</ a > for dynamic reports</ li >
< li > Build a < a href = "cli-tool.html" > CLI tool</ a > for template processing</ li >
</ ul >
2026-01-26 05:12:14 +01:00
{% endraw %}
{% endblock %}