@@ -31,7 +31,7 @@ Variables can be used in most string properties: `content`, `data`, `src`, `colo
3131
3232# # Inline Expressions
3333
34- FlexRender supports inline expressions within `{{ }}` delimiters. Expressions extend simple variable substitution with arithmetic, null coalescing, and filters.
34+ FlexRender supports inline expressions within `{{ }}` delimiters. Expressions extend simple variable substitution with arithmetic, comparison operators, logical NOT, null coalescing, and filters.
3535
3636# ## Arithmetic
3737
@@ -57,6 +57,52 @@ Arithmetic operators work on numeric values:
5757
5858Both operands must be numeric (NumberValue). Division by zero returns a null value.
5959
60+ # ## Comparison Operators
61+
62+ Comparison operators return boolean values and are primarily used in `{{#if}}` conditions:
63+
64+ | Operator | Description | Example |
65+ |----------|-------------|---------|
66+ | `==` | Equal | `{{#if status == 'paid'}}...{{/if}}` |
67+ | `!=` | Not equal | `{{#if status != 'cancelled'}}...{{/if}}` |
68+ | `<` | Less than | `{{#if stock < 5}}...{{/if}}` |
69+ | `>` | Greater than | `{{#if total > 1000}}...{{/if}}` |
70+ | `<=` | Less than or equal | `{{#if quantity <= 10}}...{{/if}}` |
71+ | `>=` | Greater than or equal | `{{#if rating >= 4}}...{{/if}}` |
72+
73+ ` ` ` yaml
74+ # Show status based on comparison
75+ - type: text
76+ content: "{{#if total > 1000}}Free shipping!{{else}}Shipping: 10${{/if}}"
77+
78+ # Check string equality
79+ - type: text
80+ content: "{{#if status == 'paid'}}Payment received{{else}}Awaiting payment{{/if}}"
81+ ` ` `
82+
83+ Comparison rules :
84+ - **Numbers**: compared by value (`100 == 100.0` is true)
85+ - **Strings**: compared using ordinal (case-sensitive) comparison
86+ - **Booleans**: only `==` and `!=` supported; ordered comparisons return false
87+ - **Null**: `null == null` is true; `null != <anything>` is true; ordered comparisons with null return false
88+ - **Mixed types** (e.g., string vs number): `==` is false, `!=` is true, ordered comparisons return false
89+
90+ # ## Logical NOT
91+
92+ The `!` operator inverts the truthiness of a value :
93+
94+ ` ` ` yaml
95+ # Show when NOT active
96+ - type: text
97+ content: "{{#if !active}}Account is inactive{{/if}}"
98+
99+ # NOT with comparison
100+ - type: text
101+ content: "{{#if !(total > 1000)}}Standard shipping{{/if}}"
102+ ` ` `
103+
104+ The `!` operator evaluates the operand for [truthiness](#conditional-blocks) and returns the opposite boolean value.
105+
60106# ## Null Coalescing
61107
62108The `??` operator provides a fallback when the left side is null or missing :
@@ -127,11 +173,12 @@ Operators are evaluated in this order (highest to lowest):
127173
128174| Precedence | Operators |
129175|------------|-----------|
130- | 1 (highest) | Unary minus (`-x`) |
176+ | 1 (highest) | Logical NOT (`!x`), Unary minus (`-x`) |
131177| 2 | Multiplication, Division (`*`, `/`) |
132178| 3 | Addition, Subtraction (`+`, `-`) |
133- | 4 | Null coalescing (`??`) |
134- | 5 (lowest) | Filter pipe (`\|`) |
179+ | 4 | Comparison (`==`, `!=`, `<`, `>`, `<=`, `>=`) |
180+ | 5 | Null coalescing (`??`) |
181+ | 6 (lowest) | Filter pipe (`\|`) |
135182
136183# ## Expression Limits
137184
@@ -173,6 +220,119 @@ public interface ITemplateFilter
173220}
174221` ` `
175222
223+ ---
224+
225+ # # Text Blocks
226+
227+ Text blocks provide control flow inside `content` strings. They are processed by the template engine at render time.
228+
229+ > **Note:** Text blocks (`{{#if}}`, `{{#each}}`) work inside text `content` values. For element-level conditions and loops, use the `if` and `for-each` element properties instead.
230+
231+ # ## Conditional Blocks
232+
233+ ```
234+ {{#if condition}}...{{/if}}
235+ {{#if condition}}...{{else}}...{{/if}}
236+ ```
237+
238+ The condition is evaluated for truthiness:
239+
240+ | Value | Truthy? |
241+ |-------|---------|
242+ | Non-empty string | Yes |
243+ | Non-zero number | Yes |
244+ | `true` | Yes |
245+ | Non-empty array | Yes |
246+ | Non-empty object | Yes |
247+ | `null` / missing key | No |
248+ | Empty string `""` | No |
249+ | `0` | No |
250+ | `false` | No |
251+ | Empty array `[]` | No |
252+
253+ Conditions support full expressions including comparison operators, logical NOT, null coalescing, arithmetic, and filters:
254+
255+ ```yaml
256+ - type: text
257+ content: "{{#if name}}Hello {{name}}{{else}}Hello guest{{/if}}"
258+
259+ - type: text
260+ content: "{{#if name ?? nickname}}Hi {{name ?? nickname}}{{/if}}"
261+
262+ - type: text
263+ content: "{{#if count}}{{count}} items{{else}}No items{{/if}}"
264+
265+ # Comparison in conditions
266+ - type: text
267+ content: "{{#if count > 0}}{{count}} items{{else}}No items{{/if}}"
268+
269+ - type: text
270+ content: "{{#if status == 'active'}}Online{{else}}Offline{{/if}}"
271+
272+ # Logical NOT in conditions
273+ - type: text
274+ content: "{{#if !disabled}}Feature enabled{{/if}}"
275+ ```
276+
277+ ### Loop Blocks
278+
279+ ```
280+ {{#each arrayPath}}...{{/each}}
281+ ```
282+
283+ Iterates over an array. Inside the loop body, the current item's properties are accessible directly. Loop variables:
284+
285+ | Variable | Type | Description |
286+ | ----------| ------| -------------|
287+ | ` @index ` | number | 0-based iteration index |
288+ | ` @first ` | bool | ` true ` for the first item |
289+ | ` @last ` | bool | ` true ` for the last item |
290+
291+ ``` yaml
292+ - type : text
293+ content : " {{#each items}}{{name}}{{#if @last}}.{{else}}, {{/if}}{{/each}}"
294+
295+ # Output with items=[{name:"A"},{name:"B"},{name:"C"}]: "A, B, C."
296+ ```
297+
298+ ### Nesting
299+
300+ Text blocks can be nested. The maximum nesting depth is controlled by ` ResourceLimits.MaxTemplateNestingDepth ` (default: 100).
301+
302+ ``` yaml
303+ - type : text
304+ content : " {{#each groups}}[{{#each items}}{{val}}{{/each}}]{{/each}}"
305+
306+ - type : text
307+ content : " {{#each users}}{{#if active}}{{name}} {{/if}}{{/each}}"
308+ ` ` `
309+
310+ ### String Literals and Escape Sequences
311+
312+ String literals in expressions support both single and double quotes:
313+
314+ ` ` `
315+ {{name ?? "default"}}
316+ {{name ?? 'default'}}
317+ ```
318+
319+ Escape sequences are supported inside string literals:
320+
321+ | Escape | Result |
322+ | --------| --------|
323+ | ` \\ ` | ` \ ` |
324+ | ` \" ` | ` " ` |
325+ | ` \' ` | ` ' ` |
326+ | ` \n ` | newline |
327+ | ` \t ` | tab |
328+
329+ ``` yaml
330+ - type : text
331+ content : " {{greeting ?? 'it\\ 's a default'}}"
332+ ` ` `
333+
334+ ---
335+
176336### Data Structure
177337
178338Data is passed as ` ObjectValue`:
0 commit comments