Syntax Guide: Customized Reporting
This article explains how to build Word (.docx) templates using the custom template language. Templates are regular Word documents with embedded commands that get replaced at report-generation time.
Most important rule: Every command must be wrapped exactly like this:
#{ … }#
Missing the#characters is the #1 cause of broken templates and can be difficult to debug.
## Table of contents
Template data model
Common paths to use
Loops - (loops over buildings, task contexts, access points)
Conditional Content (IF blocks)
Generating Images
Example Starter Template
Debugging Checklist
What data is available in a template?
When a user generates a report, they select:
-
a Site
-
one or more Building + Task Context + SSID combinations
One data object is built that your template can read. The root object looks like:
{
site: {
id,
name,
buildings: [
{
id,
name,
floorPlans: [{ id, name }],
taskContexts: [
{
id,
type: "survey" | "design",
name,
ssid,
createdBy,
accessPoints: [
{
id,
displayName,
floorPlanId,
vendor?,
mountType: "ceiling" | "wall" | "floor",
modelName?,
radios: [
{
band: "2.4" | "5" | "6",
channel,
channelWidthMhz,
bondedChannels: number[],
powerDbm?,
bsss: [
{
macAddress?,
ssid,
basicBitrates: number[],
supportedBitrates: number[],
standards: WifiStandard[],
isNonTransmitted?,
isStandardPower?
}
]
}
]
}
]
]
}
]
}
]
}
}
Common paths to use
-
site.name -
site.buildings -
$building.name -
$building.floorPlans -
$building.taskContexts -
$taskContext.name,$taskContext.type,$taskContext.ssid,$taskContext.createdBy -
$taskContext.accessPoints -
$accessPoint.displayName,$accessPoint.mountType,$accessPoint.radios -
$radio.band,$radio.channel,$radio.bsss -
$bss.ssid,$bss.macAddress
Inserting values (JavaScript expressions)
Use #{ … }# to insert values into the document. The expression inside is JavaScript.
Example: simple text fields
Site: #{ site.name }#
Example: nested properties in a loop
Building name: #{ $building.name }#
Task type: #{ $taskContext.type }#
SSID: #{ $taskContext.ssid }#$ symbol (see next section).FOR loops (repeat content)
Use FOR … IN … to loop over arrays. Inside a loop, you reference the current item with a $ prefix.
Loop over buildings
#{ FOR building IN site.buildings }#
Building: #{ $building.name }#
#{ END-FOR building }#Loop over task contexts in each building
#{ FOR building IN site.buildings }#
Building: #{ $building.name }#
#{ FOR taskContext IN $building.taskContexts }#
- Task: #{ $taskContext.name }# (#{ $taskContext.type }#)
SSID: #{ $taskContext.ssid }#
Created by: #{ $taskContext.createdBy }#
#{ END-FOR taskContext }#
#{ END-FOR building }#
Loop over access points
#{ FOR taskContext IN $building.taskContexts }#
Access Points:
#{ FOR accessPoint IN $taskContext.accessPoints }#
- #{ $accessPoint.displayName }# (Mount: #{ $accessPoint.mountType }#)
#{ END-FOR accessPoint }#
#{ END-FOR taskContext }#Loop over filtered access points
You can use Javascript to filter the access points according to your needs, for instance to loop only over access points that have radios, use:
#{ FOR taskContext IN $building.taskContexts }#
Access Points:
#{ FOR accessPoint IN $taskContext.accessPoints.filter(ap => ap.radios.length > 0) }#
- #{ $accessPoint.displayName }# (Mount: #{ $accessPoint.mountType }#)
#{ END-FOR accessPoint }#
#{ END-FOR taskContext }#IF blocks (conditional content)
Use IF … to include content only when a condition is true.
Example: only show model if it exists
#{ IF $accessPoint.modelName }#
Model: #{ $accessPoint.modelName }#
#{ END-IF }#
Example: show something only for surveys
#{ IF $taskContext.type === 'survey' }#
This section applies to surveys only.#{ END-IF }#Generating images in the DOCX
We provide a custom image(...) function that generates a report image and injects it into the document.
Syntax
#{ IMAGE image({type: "primary", band: "5", buildingId: $building.id, taskContextId: $taskContext.id, taskContextType: $taskContext.type, floorPlanId: $floorPlan.id, ssid: $taskContext.ssid }) }#
This is a command, not just a value insert. You must include
IMAGEand wrap everything with#{ … }#.
Image parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
type |
"primary" | "secondary" | "cci" |
✅ | Which report tool/output to render |
buildingId |
number | ✅ | Usually $building.id |
taskContextId |
number | ✅ | Usually $taskContext.id |
taskContextType |
"survey" | "design" |
✅ | Best practice: pass $taskContext.type |
band |
"2.4" | "5" | "6" |
✅ | Band to render |
ssid |
string | ✅ | Usually $taskContext.ssid |
floorPlanId |
number | optional | Use when the image is floor-plan specific |
accessPointId |
number | optional | Use when the image is AP-specific |
isThreeDimensional |
boolean | optional | If you want a 3D output (when supported) |
Example: one image per floor plan, per task context
#{ FOR taskContext IN $building.taskContexts }#
Task: #{ $taskContext.name }# (#{ $taskContext.type }#)
#{ FOR floorPlan IN $building.floorPlans }#
Floor plan: #{ $floorPlan.name }#
#{ IMAGE image({
type: "primary",
band: "5",
buildingId: $building.id,
taskContextId: $taskContext.id,
taskContextType: $taskContext.type,
floorPlanId: $floorPlan.id,
ssid: $taskContext.ssid
}) }#
#{ END-FOR floorPlan }#
#{ END-FOR taskContext }#
Practical “starter template” example
Copy/paste this into a Word document and adjust as needed:
Report for site: #{ site.name }#
#{ FOR building IN site.buildings }#
==================================================
Building: #{ $building.name }#
==================================================
Floor Plans:
#{ FOR floorPlan IN $building.floorPlans }#
- #{ $floorPlan.name }# (ID: #{ $floorPlan.id }#)
#{ END-FOR floorPlan }#
#{ FOR taskContext IN $building.taskContexts }#
----------------------------------------------
Task: #{ $taskContext.name }# (#{ $taskContext.type }#)
SSID: #{ $taskContext.ssid }#
Created by: #{ $taskContext.createdBy }#
Access Points:
#{ FOR accessPoint IN $taskContext.accessPoints }#
#{ $idx + 1 }#. #{ $accessPoint.displayName }# (#{ $accessPoint.mountType }#)
#{ END-FOR accessPoint }#
Primary (5 GHz) image:
#{ IMAGE image({
type: "primary",
band: "5",
buildingId: $building.id,
taskContextId: $taskContext.id,
taskContextType: $taskContext.type,
ssid: $taskContext.ssid
}) }#
#{ END-FOR taskContext }#
#{ END-FOR building }Debugging checklist
-
Did you wrap every command exactly with
#{and}#? -
Did Word convert quotes to smart quotes (
“ ”/‘ ’)? Replace with normal quotes. -
Inside loops, did you use
$variables?
Example: insideFOR building …, use$building, notbuilding. -
Do your
END-FORnames match?#{ END-FOR building }#must match#{ FOR building IN … }# -
If an image doesn’t render, confirm required fields exist:
type,band,buildingId,taskContextId,ssid.
Need Something Else?
Still looking for a different reporting feature? Submit your request here.