Beyond [ForEach:] - Meet the New [While:] Macro in ReportMagic 4.2

Introducing the [While:] Macro - Condition-Based Looping in ReportMagic

Coming in ReportMagic 4.2, we’re excited to introduce the [While:] / [EndWhile:] macro pair - a powerful new looping construct that repeats a section of your template as long as a condition remains true.

If you’ve ever needed to loop without knowing the iteration count upfront - paginated API calls, convergence logic, or building up results dynamically - [While:] is the answer. Unlike [ForEach:] which iterates over a fixed list, [While:] keeps going until you tell it to stop.


Quick Start

The simplest While loop: count down from 3.

[String: value=3, =>counter]
[While: condition={counter}>0]
    Counter is: [String: value={counter}]
    [Calculate: value={counter}-1, =>counter]
[EndWhile:]

Output:

Counter is: 3
Counter is: 2
Counter is: 1

The condition {counter}>0 is re-evaluated at the end of each iteration. When {counter} reaches 0, the loop ends.


Parameters

Parameter Required Default Description
condition Yes - Boolean expression, re-evaluated each iteration. Same syntax as [If:].
maxIterations No 10,000 Safety cap to prevent infinite loops during development.
storeAs No _whileIndex Name of the 0-based iteration counter. Only available inside the loop.
ifNoIterations No - Fallback text when the condition is false on the very first check.

Safety First: maxIterations

Worried about infinite loops? The maxIterations parameter has you covered. It defaults to 10,000 but you can set it lower during development:

[String: value=true, =>keepGoing]
[While: condition='{keepGoing}'=='true', maxIterations=5, storeAs=i]
    Iteration [String: value={i}]
[EndWhile:]

Output:

Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4

The loop would run forever (since keepGoing is never changed), but maxIterations=5 caps it safely.


Handling Zero Iterations with ifNoIterations

Sometimes the condition might be false before the loop even starts. Use ifNoIterations to provide fallback text:

[String: value=false, =>hasData]
[While: condition='{hasData}'=='true', ifNoIterations=No data available.]
    Processing data...
[EndWhile:]

Output:

No data available.

Nested While Loops

You can nest [While:] loops. Just use explicit storeAs names to avoid counter variable collisions:

[String: value=3, =>outer]
[While: condition={outer}>0, storeAs=outerIndex]
    [String: value=2, =>inner]
    [While: condition={inner}>0, storeAs=innerIndex]
        Outer [String: value={outerIndex}] Inner [String: value={innerIndex}]
        [Calculate: value={inner}-1, =>inner]
    [EndWhile:]
    [Calculate: value={outer}-1, =>outer]
[EndWhile:]

Output:

Outer 0 Inner 0
Outer 0 Inner 1
Outer 1 Inner 0
Outer 1 Inner 1
Outer 2 Inner 0
Outer 2 Inner 1

Each outer iteration resets inner to 2 and runs the inner loop to completion before continuing.


Using [Break:] to Exit Early

Need to bail out of a loop? [Break:] works inside [While:] just like it does in [ForEach:]:

[String: value=10, =>counter]
[While: condition={counter}>0]
    [Calculate: value={counter}-1, =>counter]
    [If: condition={counter}=3][Break:][EndIf:]
    Value [String: value={counter}]
[EndWhile:]

Output:

Value 9
Value 8
Value 7
Value 6
Value 5
Value 4

The loop counts down from 10. When counter hits 3, [Break:] fires before “Value 3” is printed - so the last output is “Value 4”.


Using [Continue:] to Skip Iterations

[Continue:] skips the rest of the current iteration and jumps straight to the next condition check. Here’s an example that skips even numbers:

[String: value=6, =>counter]
[While: condition={counter}>0]
    [Calculate: value={counter}-1, =>counter]
    [Calculate: value={counter}%2, =>isOdd]
    [If: condition={isOdd}=0][Continue:][EndIf:]
    Value [String: value={counter}]
[EndWhile:]

Output:

Value 5
Value 3
Value 1

Each iteration computes counter % 2. When the result is 0 (even), [Continue:] skips the output. Only odd values make it through.


Building Up Results Across Iterations

Variables you create inside the loop persist between iterations (unlike the storeAs counter which is cleaned up when the loop ends). This is great for accumulating results:

[String: value=3, =>outer]
[String: value=0, =>total]
[While: condition={outer}>0]
    [String: value={outer}, =>inner]
    [While: condition={inner}>0]
        [Calculate: value={total}+1, =>total]
        [Calculate: value={inner}-1, =>inner]
    [EndWhile:]
    [Calculate: value={outer}-1, =>outer]
[EndWhile:]
Total: [String: value={total}]

Output:

Total: 6

The total is 6 because it’s a triangular number: 3 + 2 + 1. The inner loop runs {outer} times each pass, and outer counts down from 3.

Alternative Syntax For Previous Example

This one uses the ‘shorthand’ for the [Calculate:] macro to store the outer and inner variables as integers, rather than strings. The result is the same, i.e. 6.

[=:3, =>outer] // Implicitly Int32
[=:0, =>total] // Implicitly Int32
[While: condition=`outer > 0`]
    [=: `outer`, =>inner]
    [While: condition=`inner > 0`]
        [=: `total + 1`, =>total]
        [=: `inner - 1`, =>inner]
    [EndWhile:]
    [=: `outer - 1`, =>outer]
[EndWhile:]
Total: [=: total]

Key Things to Know

  • The storeAs counter is loop-scoped. It’s available inside the body but removed when the loop ends. If you need it afterwards, copy it to another variable inside the loop.
  • Variables updated inside the body affect the next condition check. This is the fundamental mechanism - your loop body changes state, and the condition reflects it.
  • Works everywhere. RmScript, Word documents, and HTML documents all fully supported.

How It Compares to [ForEach:]

ForEach While
Use when You have a list to iterate You have a condition to check
Iteration count Known upfront Unknown / dynamic
Iterator variable Current list item 0-based counter
Break / Continue Yes Yes
Nesting Yes Yes

Think of [ForEach:] as a “for each item in this list” loop, and [While:] as a “keep going until this is no longer true” loop. They complement each other.


Coming Soon

The [While:] macro will be available in ReportMagic 4.2, arriving in the coming months. We’re looking forward to seeing what creative uses the community finds for it.

Have questions or ideas for how you’d use condition-based looping? Let us know in the comments below!