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:
- Conditionals: Showing or hiding content
- Loops: Displaying lists and using loop variables
- Pattern matching: Handling enums and options
- Filters: Formatting and transforming data
- Hands-on practice and exercises
- 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 letin{% if %};Option<String>does not implementDisplay, 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.indexstarts at 1;loop.index0starts 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. Useitems.len()for a length; replacement needs a Rust helper/custom filter (there is no built-inreplacefilter in Askama 0.16).
Escaping and the |safe Filter
By default, Askama escapes HTML. Use |safe only for trusted content.
Common Misconceptions
- Misconception: “I can use any method or field in a template.” Reality: Only fields and methods exposed by the struct are available.
- 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:
- Add an
Option<String>field to your template struct. - Update your template to show a different message if the name is
None. - Render with both
SomeandNonevalues.
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.indexand{% 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>andname: Stringto your struct - Use
{% match %}to show different badges for status - Use
|capitalizeon the name
Extension Ideas:
- Add a list of messages and use
|truncateor|join - Try other filters like
titleortrim, or useitems.len()
7. Common Pitfalls and Troubleshooting (10 minutes)
Frequent Mistakes
- Problem: Using a field or method not in the struct Cause: Typo or missing field Solution: Check struct and template for matching names
- 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 buildto 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
- Use conditionals, loops, and pattern matching for dynamic content
- Filters help format and transform data
- Handle empty and optional data gracefully
Quick Review Questions
- How do you show a message only if a value is present?
- What does
{% else %}do in a loop? - 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