Boolean Expressions Guide¶
Boolean expressions allow you to evaluate complex logical conditions directly within placeholders. Combine variables with operators to create dynamic, conditional content without using separate {{#if}} blocks.
Table of Contents¶
- Quick Start
- Expression Syntax
- Logical Operators
- Comparison Operators
- Membership, String, and Existence Operators
- Reserved Words and Literal Quoting
- Differences from
{{#if}}Conditions - Combining with Format Specifiers
- Advanced Usage
- Best Practices
- Real-World Examples
- Operator Precedence
Quick Start¶
Wrap any boolean expression in parentheses within a placeholder:
Template:
C# Data:
JSON Data:
Output:
Expression Syntax¶
Basic Rules¶
- Expressions must start with
(- This distinguishes them from simple variables - Expressions must end with
)- Properly close the parentheses - Operators are case-insensitive -
and,AND,Andall work - Word operators need spaces -
(var1 and var2)works,(var1and var2)does not (var1andis read as one name). Symbol operators do not need spaces:(Count>0)works - No
}inside the expression - the placeholder ends at the first} - Result is a boolean - rendered as
True/False, or through a boolean format specifier such as:yesno
If an expression cannot be evaluated (for example {{(Count >)}} or {{(A && B)}}), it is handled like a missing variable: by default the placeholder stays in the document, and processing reports an ExpressionFailed and a MissingVariable warning.
Expression vs. Simple Variable¶
Simple Variable (no evaluation):
Expression (evaluated):
Complex Expression:
Logical Operators¶
AND Operator¶
Returns true only if both operands are true.
Syntax: and
Truth Table:
| Left | Right | Result |
|---|---|---|
| true | true | true |
| true | false | false |
| false | true | false |
| false | false | false |
Template:
C# Data:
JSON Data:
Output:
OR Operator¶
Returns true if at least one operand is true.
Syntax: or
Truth Table:
| Left | Right | Result |
|---|---|---|
| true | true | true |
| true | false | true |
| false | true | true |
| false | false | false |
Template:
C# Data:
var data = new Dictionary<string, object>
{
["HasPermissionA"] = false,
["HasPermissionB"] = true
};
JSON Data:
Output:
NOT Operator¶
Returns the opposite of the operand.
Syntax: not
Truth Table:
| Value | Result |
|---|---|
| true | false |
| false | true |
Template:
C# Data:
JSON Data:
Output:
Comparison Operators¶
Equality (== or =)¶
Checks if two values are equal. Both == and = are supported.
Numbers are compared by value across numeric types, so {{(Id = 5)}} is True when Id is 5L and {{(Price > 5)}} is True when Price is the decimal 10.5 or JSON 10.5. Inline expressions are stricter than {{#if}} in some cases; see Differences from {{#if}} Conditions.
Template:
C# Data:
JSON Data:
Output:
Inequality (!=)¶
Checks if two values are not equal.
Template:
Data:
Output:
Greater Than (>)¶
Checks if left value is greater than right value.
Template:
Data:
Output:
Greater Than or Equal (>=)¶
Checks if left value is greater than or equal to right value.
Template:
Data:
Output:
Less Than (<)¶
Checks if left value is less than right value.
Template:
C# Data:
JSON Data:
Output:
Less Than or Equal (<=)¶
Checks if left value is less than or equal to right value.
Template:
Data:
Output:
Membership, String, and Existence Operators¶
Membership (in)¶
Checks whether a value is a member of a collection. The right side can be a collection variable, a list literal in parentheses, or a comma-separated string.
Template:
Data:
Output:
Negate with not: {{(not Role in Roles)}}.
String Checks (contains, startswith, endswith)¶
Template:
Data:
Output:
These comparisons are case-sensitive, like = and ==.
When the left side is a list, contains checks membership ({{(Tags contains "urgent")}} is True when Tags is ["urgent", "new"]), exactly like "urgent" in Tags. startswith / endswith on a list are always False.
Existence (exists, is empty, is not empty)¶
exists checks that a variable is present regardless of its value; is empty / is not empty check whether a value is missing, null, blank, or an empty collection.
Template:
Data:
Output:
Reserved Words and Literal Quoting¶
and, or, not, true, false and null are always keywords. The operator words added in 1.7.0 (in, contains, startswith, endswith, exists, is, empty) are keywords only where an operator is expected; where a value is expected they are read as variable names, so a variable named Exists or Empty keeps working.
To refer to a variable whose name is a keyword, put it in square brackets. This works for every keyword:
Unquoted words fall back to text. In a comparison, a word that is not a variable in your data is compared as its own text: {{(Status = Active)}} works like {{(Status = "Active")}} as long as there is no variable named Active. Both sides are resolved this way, so in {{(A = B)}} both A and B are looked up in your data. Quote a side ({{(A = "B")}}) when you mean the literal text. Always quote text values: an unquoted word silently changes meaning when a variable with that name is added to the data, and a misspelled variable name is compared as text instead of being reported.
Quotes: use double quotes ("text") or, in inline expressions only, single quotes ('text'). Inside a quoted value, \" is a quote and \\ a backslash. Word's typographic quotes (“…”, ‘…’) are converted to straight quotes automatically.
Differences from {{#if}} Conditions¶
Inline {{(...)}} expressions support the same operators and precedence as {{#if}}, but evaluate some values differently:
{{#if ...}} |
{{(...)}} |
|
|---|---|---|
A value used on its own ({{#if Count}} / {{(Count)}}) |
Truthy unless null, false, empty text, "false", "0", numeric zero, NaN or an empty list |
Only the boolean true is true; numbers and text (even "true") are False |
= with a number and numeric text (Age = 25 with "Age": "25") |
True (compared as text) | False (a number never equals text) |
= with booleans and text (IsActive = "True") |
True (case-insensitive) | False |
>, <, >=, <= with numeric text ("25" > 18) |
Numeric text is read as a number | Numbers compare as numbers; texts compare with each other; a number and text are not comparable (false) |
Single-quoted text ('Active') |
Not a string | A string |
Numbers of different types (5, 5L, 5.0m, JSON numbers) compare by value in both. in, contains, startswith, endswith, exists and is empty behave the same in both.
Combining with Format Specifiers¶
The real power comes from combining expressions with format specifiers for human-readable output.
Basic Combination¶
Template:
Data:
Output:
Complex Expression with Checkbox¶
Template:
Data:
Output:
Multiple Conditions with Checkmark¶
Template:
C# Data:
JSON Data:
Output:
OR Logic with Format¶
Template:
Data:
Output:
Advanced Usage¶
Nested Expressions¶
Use nested parentheses to control evaluation order.
Template:
C# Data:
JSON Data:
Output:
Evaluation:
1. (var1 or var2) → (true or false) → true
2. (true and var3) → (true and true) → true
Complex Nested Expressions¶
Template:
Data:
Output:
With Nested Properties¶
Template:
C# Data:
var data = new Dictionary<string, object>
{
["User"] = new
{
IsActive = true,
Profile = new
{
IsComplete = false
}
}
};
JSON Data:
Output:
In Loops¶
Use expressions within loop bodies for dynamic row-level logic.
Template:
C# Data:
var data = new Dictionary<string, object>
{
["Employees"] = new[]
{
new { Name = "Alice", IsActive = true, HasCompletedTraining = true },
new { Name = "Bob", IsActive = true, HasCompletedTraining = false },
new { Name = "Carol", IsActive = false, HasCompletedTraining = true }
}
};
JSON Data:
{
"Employees": [
{ "Name": "Alice", "IsActive": true, "HasCompletedTraining": true },
{ "Name": "Bob", "IsActive": true, "HasCompletedTraining": false },
{ "Name": "Carol", "IsActive": false, "HasCompletedTraining": true }
]
}
Output:
Comparing Numbers from Different Sources¶
Template:
Data:
Output:
String Comparisons¶
Template:
Data:
Output:
Best Practices¶
1. Use Expressions for Simple Logic¶
For inline boolean checks, expressions are cleaner than separate conditionals.
Good (concise):
Less Good (verbose):
{{#if IsActive}}
{{#if IsVerified}}
Status: ☑
{{/if}}
{{/if}}
{{#if (not IsActive)}}
Status: ☐
{{/if}}
{{#if (not IsVerified)}}
Status: ☐
{{/if}}
2. Use Conditionals for Complex Content¶
When you need to show/hide entire blocks of content, use {{#if}} instead.
Good:
{{#if IsActive}}
Account Details:
- Name: {{Name}}
- Email: {{Email}}
- Joined: {{JoinDate}}
{{/if}}
Not Recommended:
3. Keep Expressions Readable¶
Break complex logic into multiple lines or use separate variables.
Good:
Less Readable:
Status: {{(((Age >= 18) and (HasLicense or HasPermit)) and (not IsBlacklisted) and (CreditScore > 600)):checkmark}}
Better Approach:
// In your C# code:
var data = new Dictionary<string, object>
{
["Age"] = 25,
["HasLicense"] = true,
["HasPermit"] = false,
["IsBlacklisted"] = false,
["CreditScore"] = 650,
["IsQualified"] = (25 >= 18) && (true || false) && !false && (650 > 600)
};
Template:
4. Use Meaningful Variable Names¶
Clear variable names make expressions self-documenting.
Good:
Less Clear:
5. Consider Evaluation Cost¶
Complex expressions are evaluated every time they're encountered. For frequently used values, pre-calculate in your data.
Efficient:
Template:
Real-World Examples¶
Approval Workflow Document¶
Template:
Approval Request
================
Request Details:
- Submitted by: {{Requester.Name}}
- Amount: ${{Amount}}
- Date: {{SubmittedDate}}
Approval Status:
- Manager Approval: {{(ManagerApproved):checkbox}}
- Director Approval: {{(DirectorApproved):checkbox}}
- CFO Approval Required: {{(Amount > 10000):yesno}}
- CFO Approved: {{(CFOApproved or Amount <= 10000):checkbox}}
Final Status: {{((ManagerApproved and DirectorApproved) and (CFOApproved or Amount <= 10000)):checkmark}}
C# Data:
var data = new Dictionary<string, object>
{
["Requester"] = new { Name = "John Smith" },
["Amount"] = 15000,
["SubmittedDate"] = "2025-01-15",
["ManagerApproved"] = true,
["DirectorApproved"] = true,
["CFOApproved"] = true
};
JSON Data:
{
"Requester": { "Name": "John Smith" },
"Amount": 15000,
"SubmittedDate": "2025-01-15",
"ManagerApproved": true,
"DirectorApproved": true,
"CFOApproved": true
}
Employee Benefits Eligibility¶
Template:
Benefits Eligibility Report
===========================
Employee: {{Employee.Name}}
Hire Date: {{Employee.HireDate}}
Employment Status: {{Employee.Status}}
Eligibility:
- Health Insurance: {{(Employee.Status == "Full-Time" and Employee.TenureMonths >= 1):yesno}}
- 401(k) Match: {{(Employee.Status == "Full-Time" and Employee.TenureMonths >= 6):yesno}}
- Stock Options: {{(Employee.Status == "Full-Time" and Employee.TenureMonths >= 12):yesno}}
- Gym Membership: {{(Employee.Status != "Contractor"):yesno}}
Data:
{
"Employee": {
"Name": "Sarah Johnson",
"HireDate": "2024-08-15",
"Status": "Full-Time",
"TenureMonths": 5
}
}
System Requirements Check¶
Template:
System Requirements Check
=========================
{{#foreach Requirements}}
{{Name}}: {{(IsMet):checkmark}}
{{/foreach}}
Overall Status: {{((CPUMeetsReq and MemoryMeetsReq) and DiskMeetsReq):active}}
Installation Ready: {{(((CPUMeetsReq and MemoryMeetsReq) and DiskMeetsReq) and OSSupported):yesno}}
C# Data:
var data = new Dictionary<string, object>
{
["Requirements"] = new[]
{
new { Name = "CPU: 2GHz or faster", IsMet = true },
new { Name = "RAM: 8GB minimum", IsMet = true },
new { Name = "Disk: 50GB free space", IsMet = false },
new { Name = "OS: Windows 10 or later", IsMet = true }
},
["CPUMeetsReq"] = true,
["MemoryMeetsReq"] = true,
["DiskMeetsReq"] = false,
["OSSupported"] = true
};
JSON Data:
{
"Requirements": [
{ "Name": "CPU: 2GHz or faster", "IsMet": true },
{ "Name": "RAM: 8GB minimum", "IsMet": true },
{ "Name": "Disk: 50GB free space", "IsMet": false },
{ "Name": "OS: Windows 10 or later", "IsMet": true }
],
"CPUMeetsReq": true,
"MemoryMeetsReq": true,
"DiskMeetsReq": false,
"OSSupported": true
}
Project Risk Assessment¶
Template:
Project Risk Assessment
=======================
Project: {{Project.Name}}
Risk Indicators:
- Behind Schedule: {{(Project.DaysOverdue > 0):yesno}}
- Over Budget: {{(Project.SpentPercentage > 100):yesno}}
- Low Team Morale: {{(Project.MoraleScore < 3):yesno}}
- High Complexity: {{(Project.ComplexityScore >= 8):yesno}}
Risk Level:
- Low Risk: {{((Project.DaysOverdue <= 0) and (Project.SpentPercentage <= 100)):checkbox}}
- Medium Risk: {{((Project.DaysOverdue > 0 or Project.SpentPercentage > 100) and Project.MoraleScore >= 3):checkbox}}
- High Risk: {{((Project.DaysOverdue > 5 or Project.SpentPercentage > 120) or Project.MoraleScore < 3):checkbox}}
Data:
{
"Project": {
"Name": "Website Redesign",
"DaysOverdue": 3,
"SpentPercentage": 95,
"MoraleScore": 4,
"ComplexityScore": 7
}
}
Operator Precedence¶
From loosest to tightest binding (the same for {{#if}} and inline expressions):
- OR
or- loosest - AND
and - NOT
not - Comparison
>,>=,<,<=,=,==,!=,in,contains,startswith,endswith - Postfix
exists,is empty,is not empty- tightest; they attach directly to the value before them (e.g.Notes is empty)
Parentheses () group explicitly and override all of the above. and always binds tighter than or, and a comparison is evaluated before not is applied to it.
Examples¶
Expression: var1 or var2 and var3
Evaluation: var1 or (var2 and var3) ← AND binds tighter
Expression: (var1 or var2) and var3
Evaluation: Parentheses force OR first
Expression: not var1 and var2
Evaluation: (not var1) and var2 ← NOT binds tighter than AND
Expression: not Status = "Active"
Evaluation: not (Status = "Active") ← the comparison is evaluated first
Summary¶
Boolean expressions enable powerful inline logic in your templates:
- ✅ Logical operators:
and,or,not - ✅ Comparison operators:
=,==,!=,>,>=,<,<= - ✅ Membership, string, and existence operators:
in,contains,startswith,endswith,exists,is empty,is not empty - ✅ Nested expressions with parentheses
- ✅ Combine with format specifiers for readable output
- ✅ Works with nested properties and arrays
- ✅ Use in loops and conditionals
- ✅ Same data works with C# Dictionaries or JSON
- ✅ Case-insensitive operators
For more information, see: - Format Specifiers Guide - Format boolean output - API Reference - Complete API documentation - Examples - More code examples - FAQ - Common questions