Dynamic Content in Askama: Control Flow, Loops & Filters

Learning Objectives: By the end of this lesson, you will be able to:

  • Use conditionals, loops, and pattern matching in Askama templates
  • Apply built-in filters to format and transform data
  • Avoid common mistakes when working with dynamic content in templates

1. Welcome and Introduction (5 minutes)

Icebreaker Activity

Have you ever needed to show a list of items or a personalized message on a web page? How do you handle empty lists or missing data in your templates?

Lesson Overview

  • Duration: 75-90 minutes
  • Prerequisites: Day 1 lesson (Askama setup, basic templating)
  • Materials Needed:
    • Rust toolchain
    • Code editor
    • Askama project from Day 1

2. Learning Objectives and Session Outline (5 minutes)

What We’ll Cover Today:

  1. Conditionals: Showing or hiding content
  2. Loops: Displaying lists and using loop variables
  3. Pattern matching: Handling enums and options
  4. Filters: Formatting and transforming data
  5. Hands-on practice and exercises
  6. Common pitfalls and troubleshooting

Success Criteria:

By the end of this session, you should be able to use control flow, loops, and filters to render dynamic content in Askama templates.

3. Foundation Knowledge (15-20 minutes)

Core Concepts

Conditionals in Templates

Conditionals let you show or hide parts of your template based on data. In Askama, you use {% if %} blocks, just like in Rust or Jinja.

Real-World Analogy: Think of conditionals as traffic lights: they control which path your template takes based on the data.

Why This Matters:

  • Show personalized greetings only if a user is logged in
  • Hide empty sections (like an empty shopping cart)

Loops in Templates

Loops let you repeat a section for each item in a list. Use {% for item in items %} to iterate over vectors, slices, or iterators.

Real-World Analogy: Like a chef preparing a plate for each guest at a table, loops let you generate repeated content for each data item.

Pattern Matching

Askama supports Rust-style pattern matching with {% match %} blocks. This is useful for handling enums or Option types.

Filters

Filters let you format or transform data before displaying it. For example, you can capitalize a name or join a list into a string.

Key Terminology

  • Conditional: A block that renders only if a condition is true
  • Loop: A block that repeats for each item in a collection
  • Filter: A function that transforms template output (e.g., |upper)
  • Pattern Matching: Handling different data shapes (like Some/None)

4. Detailed Explanation and Examples (25-30 minutes)

Understanding Dynamic Content in Askama

Conditionals

{% if let Some(name) = user_name %}
  <p>Welcome, {{ name|upper }}!</p>
{% else %}
  <p>Welcome, guest!</p>
{% endif %}
  • Use Rust boolean expressions or if let in {% if %}; Option<String> does not implement Display, so bind its inner value before printing it.

Loops

<ul>
{% for item in items %}
  <li>{{ loop.index }}. {{ item|capitalize }}</li>
{% else %}
  <li>No items found.</li>
{% endfor %}
</ul>
  • loop.index starts at 1; loop.index0 starts at 0
  • {% else %} runs if the list is empty

Pattern Matching

{% match status %}
  {% when Some("active") %}
    <span class="active">Active</span>
  {% when Some("inactive") %}
    <span class="inactive">Inactive</span>
  {% else %}
    <span>Unknown</span>
{% endmatch %}
  • Use for enums or Option<T>

Filters

<p>{{ name|capitalize }}</p>
<p>{{ description|truncate(20) }}</p>
<p>{{ items|join(", ") }}</p>
  • Common built-in filters: capitalize, upper, lower, truncate, join, title, escape, safe. Use items.len() for a length; replacement needs a Rust helper/custom filter (there is no built-in replace filter in Askama 0.16).

Escaping and the |safe Filter

By default, Askama escapes HTML. Use |safe only for trusted content.

Common Misconceptions

  1. Misconception: “I can use any method or field in a template.” Reality: Only fields and methods exposed by the struct are available.
  2. Misconception: “Filters can do anything.” Reality: Only built-in and registered custom filters are available.

5. Live Coding Demonstration (20-25 minutes)

Example 1: Conditional Greeting

// main.rs
use askama::Template;
#[derive(Template)]
#[template(path = "greeting.html")]
struct GreetingTemplate {
    user_name: Option<String>,
}

fn main() {
    let template = GreetingTemplate { user_name: Some("Bob".to_string()) };
    println!("{}", template.render().unwrap());
}
<!-- templates/greeting.html -->
{% if let Some(name) = user_name %}
  <h1>Hello, {{ name|upper }}!</h1>
{% else %}
  <h1>Hello, guest!</h1>
{% endif %}

Example 2: Looping Over Items

use askama::Template;
#[derive(Template)]
#[template(path = "list.html")]
struct ListTemplate {
    items: Vec<String>,
}

fn main() {
    let template = ListTemplate { items: vec!["apple".into(), "banana".into()] };
    println!("{}", template.render().unwrap());
}
<!-- templates/list.html -->
<ul>
{% for item in items %}
  <li>{{ loop.index }}. {{ item|capitalize }}</li>
{% else %}
  <li>No items found.</li>
{% endfor %}
</ul>

Example 3: Pattern Matching and Filters

use askama::Template;
#[derive(Template)]
#[template(path = "status.html")]
struct StatusTemplate {
    status: Option<&'static str>,
}

fn main() {
    let template = StatusTemplate { status: Some("active") };
    println!("{}", template.render().unwrap());
}
<!-- templates/status.html -->
{% match status %}
  {% when Some("active") %}
    <span class="active">Active</span>
  {% when Some("inactive") %}
    <span class="inactive">Inactive</span>
  {% else %}
    <span>Unknown</span>
{% endmatch %}

Debugging Demonstration

Show common errors and how to fix them:

<!-- Incorrect: using a field not in the struct -->
<p>{{ username }}</p>
<!-- Error: field `username` not found -->

<!-- Corrected: use the right field -->
<p>{{ user_name }}</p>

6. Hands-On Practice (30-40 minutes)

Exercise 1: Basic Practice (10 minutes)

Objective: Use a conditional to show a personalized or guest greeting.

Instructions:

  1. Add an Option<String> field to your template struct.
  2. Update your template to show a different message if the name is None.
  3. Render with both Some and None values.

Starter Code:

#[derive(Template)]
#[template(path = "greeting.html")]
struct GreetingTemplate {
    user_name: Option<String>,
}

Solution:

fn main() {
    let t1 = GreetingTemplate { user_name: Some("Eve".to_string()) };
    let t2 = GreetingTemplate { user_name: None };
    println!("{}", t1.render().unwrap());
    println!("{}", t2.render().unwrap());
}

Exercise 2: Intermediate Challenge (15 minutes)

Objective: Display a numbered list of items, handling the empty case.

Requirements:

  • Add a Vec<String> field to your struct
  • Use a loop in your template to display each item with its number
  • Show a message if the list is empty

Hints:

  • Use loop.index and {% else %}

Exercise 3: Creative Application (15 minutes)

Objective: Use pattern matching and filters to display user status and format names.

Challenge:

  • Add a status: Option<&'static str> and name: String to your struct
  • Use {% match %} to show different badges for status
  • Use |capitalize on the name

Extension Ideas:

  • Add a list of messages and use |truncate or |join
  • Try other filters like title or trim, or use items.len()

7. Common Pitfalls and Troubleshooting (10 minutes)

Frequent Mistakes

  1. Problem: Using a field or method not in the struct Cause: Typo or missing field Solution: Check struct and template for matching names
  2. Problem: Forgetting {% else %} in loops Cause: Empty lists render nothing Solution: Use {% else %} to handle empty cases

Debugging Tips

  • Read error messages for missing fields or syntax errors
  • Use cargo build to catch template errors early
  • Test with empty and non-empty data

8. Best Practices and Professional Tips (5 minutes)

Industry Standards

  • Keep logic simple in templates; do complex work in Rust
  • Use filters for formatting, not business logic
  • Always handle empty lists and optional data

Next Steps in Your Learning Journey

  • Explore template inheritance and includes
  • Try custom filters and macros
  • Read the Askama Book for advanced control flow

9. Summary and Review (5 minutes)

Key Takeaways

  1. Use conditionals, loops, and pattern matching for dynamic content
  2. Filters help format and transform data
  3. Handle empty and optional data gracefully

Quick Review Questions

  1. How do you show a message only if a value is present?
  2. What does {% else %} do in a loop?
  3. Name two filters and what they do.

For Instructors

Teaching Tips

  • Show both empty and non-empty data cases
  • Encourage students to experiment with filters and control flow
  • Use real error messages to teach debugging

Assessment Ideas

  • Ask students to add new fields and use them in templates
  • Review code for correct use of loops and filters
  • Peer review: swap templates and structs to check for errors

Adaptation Notes

  • For beginners: provide more code scaffolding and filter examples
  • For advanced students: challenge them to use pattern matching and custom filters
  • Online: use live coding and screen sharing for exercises