Conditional Rendering
Conditional blocks let parts of a template appear only when the data supports them. You wrap a region of the document in {% if condition %} ... {% endif %} and AdviceDocs decides at document-fill time whether to keep or drop that region. Anything inside a conditional block is also flagged as out of scope when the condition is false, so the user is never asked to fill data that won’t appear in the final document.
How conditional blocks are written
Conditional syntax follows Jinja2. The full set of forms below is supported by the template parser.
Plain if / endif
{% if has_partner %}
Partner name: {{ partner.name }}
{% endif %}if / else
{% if is_couple %}
This advice applies to both members of the couple.
{% else %}
This advice applies to a single client.
{% endif %}if / elif / else
{% if risk_profile == "Conservative" %}
Conservative recommendations apply.
{% elif risk_profile == "Balanced" %}
Balanced recommendations apply.
{% elif risk_profile == "Growth" %}
Growth recommendations apply.
{% else %}
Default risk recommendations apply.
{% endif %}Negation, boolean composition, comparisons, tests
The expression inside an if can use any of the standard Jinja2 operators.
- Negation —
{% if not insurance %} - Boolean composition —
{% if has_partner and partner.is_employed %},{% if has_super or has_smsf %} - Comparisons —
==,!=,<,>,<=,>= - Membership —
in,not in - Tests —
is defined,is none,is not none,is true,is false - Truthy checks —
{% if amount %}evaluates to true for non-zero numbers, non-empty strings, non-empty lists, and so on.
{% if amount > 1000 %} ... {% endif %}
{% if status == "approved" %} ... {% endif %}
{% if client.age >= 65 %} ... {% endif %}
{% if not insurance %} ... {% endif %}
{% if has_partner and partner.has_employment %} ... {% endif %}
{% if risk_profile in ["Growth", "Aggressive"] %} ... {% endif %}
{% if recommended_amount is defined %} ... {% endif %}
{% if existing_plan is none %} ... {% endif %}Comparisons can target any combination of fields, nested fields, string literals, and numeric literals. The left-hand side is treated as the condition field and is what gets recorded as the dependency for any nested fields.
Table-cell and table-row conditionals
Word tables can’t cleanly host a regular {% if %} block, so AdviceDocs adds two custom tags that the parser normalises into standard if-blocks before it walks the AST:
- Table cell —
{% tc if condition %} ... {% endtc %}hides the cell’s content (or the whole column) when the condition is false. - Table row —
{% tr if condition %} ... {% endtr %}hides the entire row when the condition is false.
{% tr if has_super %}
| Super | {{ super_balance }} |
{% endtr %}
{% tc if show_price %}
{{ price }}
{% endtc %}Behaviour is identical to {% if %} — the same operators, the same dependency tracking. Use tr/tc only when the conditional region sits inside a Word table; everywhere else, plain {% if %} works fine.
How condition dependencies are tracked
On upload, AdviceDocs walks the template’s AST and stores a list of conditions against every field that lives inside a conditional block. This list is the field’s condition_dependencies.
Outer-to-inner ordering
Dependencies are recorded outermost first, innermost last. Nested blocks accumulate — a field inside two nested ifs gets both conditions.
{% if has_partner %}
{% if partner.has_employment %}
{{ partner.employer }}
{% endif %}
{% endif %}
# partner.employer.condition_dependencies = ["has_partner", "partner.has_employment"]Negation is recorded as ! prefix
When the condition is {% if not X %}, the stored dependency is !X, so the truthy/falsy direction is preserved.
{% if not insurance %}
{{ insurance_not_scope_reason }}
{% endif %}
# insurance_not_scope_reason.condition_dependencies = ["!insurance"]Compound conditions store the primary field
For {% if X and Y %} or {% if X or Y %}, the parser records the left-hand operand (X) as the condition field. The right-hand side is parsed but not stored as a dependency.
What happens at document-fill time
- Stage 1 of extraction evaluates each field’s
condition_dependenciesagainst the just-extracted data. - If any dependency evaluates false, the field is marked
out_of_scope = TRUEand its value is left null. - Subsequent extraction stages skip out-of-scope fields entirely — no LLM calls are spent on data that won’t appear.
- The field-review UI hides out-of-scope fields, so the user is never asked to fill them.
- At render time, Jinja2 also evaluates the
{% if %}block and drops the region from the document.
Visual cue in the editor
{{has_partner}} or {{!insurance}}. The badge tooltip reads “Depends on [field] being truthy”, making it easy to trace why a field appeared or disappeared.Nested if blocks
Nesting works as you’d expect. There is no fixed depth limit; conditions accumulate in outer-to-inner order, and a child field is only in scope when every ancestor condition is true.
{% if has_partner %}
{% if partner.has_super %}
{% if partner.super.balance > 100000 %}
{{ partner.super.recommended_strategy }}
{% endif %}
{% endif %}
{% endif %}Other control structures
Alongside {% if %} blocks, AdviceDocs supports a small subset of Jinja2.
- Supported —
{% for %}loops (with{% else %}for the empty case),{% tr for %}for table-row loops. - Not supported —
{% set %},{% block %},{% include %},{% import %},{% macro %}, and Jinja2 filters such as{{ x | upper }}. (Use{{ X }}in ALL CAPS to force uppercase output instead.)
Common pitfalls
Missing endif
{% if %} needs a matching {% endif %} and every {% tc if %} needs {% endtc %}, every {% tr if %} needs {% endtr %}. An unbalanced block raises a Jinja2 syntax error at upload time and the field list won’t generate.Use == not =
{% if status == "approved" %} is correct; {% if status = "approved" %} is a syntax error.Curly quotes from Word
" to curly quotes “ ”. AdviceDocs normalises these for you on upload, but you can avoid the warning by typing comparisons in a plain-text editor or by toggling off Word’s smart-quote autocorrect.Typo: {{%endif
{{%endif%}} (double curly instead of curly-percent) is a surprisingly common typo. The parser detects this specific case and returns a friendly error pointing at the offending line.Compound conditions only track the left operand
{% if X and Y %}{{ inner }}{% endif %}, only X is stored as the condition dependency on inner. If you rely on Y driving scope behaviour at fill time, prefer nesting two if-blocks instead so each condition is recorded explicitly.Syntax to copy
Drop these into a template document and adapt the field names and literals to your data.
{% if has_partner %} ... {% endif %}
{% if partner.is_employed %} ... {% endif %}
{% if not insurance %} ... {% endif %}
{% if recommended_amount is defined %} ... {% endif %}
{% if existing_plan is none %} ... {% endif %}{% if status == "approved" %} ... {% endif %}
{% if risk_profile != "Conservative" %} ... {% endif %}
{% if risk_profile in ["Growth", "Aggressive"] %} ... {% endif %}{% if amount > 1000 %} ... {% endif %}
{% if client.age >= 65 %} ... {% endif %}
{% if balance < recommended_minimum %} ... {% endif %}{% if has_partner and partner.has_employment %} ... {% endif %}
{% if has_super or has_smsf %} ... {% endif %}
{% if not (status == "rejected") %} ... {% endif %}{% if is_couple %}
Couple advice
{% else %}
Single advice
{% endif %}
{% if risk_profile == "Conservative" %}
Conservative recommendations
{% elif risk_profile == "Balanced" %}
Balanced recommendations
{% else %}
Default recommendations
{% endif %}{% tr if has_super %}
| Super | {{ super_balance }} |
{% endtr %}
{% tc if show_price %}
{{ price }}
{% endtc %}