Nested Loops
A nested loop is a loop inside another loop — for example a list of asset categories where each category contains a list of assets. AdviceDocs walks each level, records the full path to every field, and tracks the relationship between the outer and inner collections so that the field configuration UI shows them as a clean parent / child / grandchild tree.
How to write nested loops
Two levels
Open the outer loop, open the inner loop, write the placeholders, then close the inner loop and the outer loop. Indent so each opening tag has a matching closing tag at the same level.
{% for category in categories %}
### {{ category.name }}
{% for asset in category.assets %}
- {{ asset.name }}: {{ asset.value }}
{% endfor %}
{% endfor %}
Three or more levels
There is no fixed depth limit. Each level adds another {% endfor %} at the bottom and another segment to the dot-path that AdviceDocs records.
{% for client in clients %}
{% for account in client.accounts %}
{% for transaction in account.transactions %}
{{ transaction.date }} — {{ transaction.amount }}
{% endfor %}
{% endfor %}
{% endfor %}
Mixing with conditionals
You can wrap an inner loop in an {% if %} block. Fields inside the inner loop accumulate both the outer collection path and the condition dependency, so they only appear when the condition is true.
{% for client in clients %}
{% if client.has_super %}
{% for fund in client.super_funds %}
- {{ fund.name }} ({{ fund.balance }})
{% endfor %}
{% endif %}
{% endfor %}
Nested loops and table rows
If the inner loop produces table rows, use {% tr for %} at the row level — not plain {% for %} — so Word’s row XML stays intact. A regular outer loop wrapped around a table-row loop works fine.
{% for category in categories %}
**{{ category.name }}**
Asset | Value |
|---|---|
{% tr for asset in category.assets %} | |
{{ asset.name }} | {{ asset.value }} |
{% endtr %} | |
{% endfor %}
How nested fields are recorded
At parse time AdviceDocs keeps a stack of active loop variables. As each loop opens, it pushes the variable name and the collection it iterates over. When a field reference starts with one of those variables, it is rewritten to the full collection path.
For the two-level example above, three fields are recorded:
categories (is_array = true)
categories.assets (is_array = true)
categories.assets.name (scalar leaf)
categories.assets.value (scalar leaf)
The dot-notation path is what shows up in the field configuration tree. Each parent appears as an array; each leaf inherits its type from the standard inference rules (currency, date, etc.).
Loop variable scoping
Standard Jinja2 scoping applies:
- The inner loop can reference the outer loop’s variable. Inside the inner block, both
categoryandassetare visible. - The outer loop variable is not visible after the outer loop ends.
- Both
loop.index,loop.first, etc. refer to the nearest enclosing loop. To reference the outer loop counter from inside an inner loop, capture it before opening the inner loop, or use{% set %}... except — AdviceDocs does not support{% set %}. Workaround: include the outer index in the data shape so it’s available as a field.
Don't shadow loop variables
{% for product in products %}{% for product in product.alternatives %}) is allowed by Jinja2 but silently confusing. Once the inner loop opens, the outer product is no longer reachable. Use distinct names: {% for product in products %}{% for alt in product.alternatives %}.Configuration after upload
Each level shows up in the field tree under its parent. You can give the parents descriptions, set types on the leaves (currency, date, etc.), and attach condition dependencies. There are no nested-loop-specific settings — the loop is purely a template-time construct.
Common pitfalls
Match every endfor to its for
{% for %} must close with its own {% endfor %}. Each {% tr for %} must close with {% endtr %}. Crossed or missing closes are syntax errors that block the field list from generating.Plain {% for %} doesn't work for nested table rows
{% tr for %}, not plain {% for %}. The outer wrapper can be either, depending on whether it spans rows or paragraphs.Loop variable typo warnings work across nesting
No depth limit, but watch performance
Syntax to copy
{% for category in categories %}
{{ category.name }}
{% for asset in category.assets %}
- {{ asset.name }}: {{ asset.value }}
{% endfor %}
{% endfor %}
{% for client in clients %}
{% for account in client.accounts %}
{% for transaction in account.transactions %}
{{ transaction.date }} — {{ transaction.amount }}
{% endfor %}
{% endfor %}
{% endfor %}
{% for category in categories %}
**{{ category.name }}**
{% tr for asset in category.assets %} | |
{{ asset.name }} | {{ asset.value }} |
{% endtr %} | |
{% endfor %}
{% for client in clients %}
{% if client.has_super %}
{% for fund in client.super_funds %}
- {{ fund.name }}: {{ fund.balance }}
{% endfor %}
{% endif %}
{% endfor %}