Calculated properties in DOCX templates

Calculated properties are powerful tools that let you modify your source JSON object before it is passed to your template for generation. Think of them as variables that let you create new pieces of information based on existing data.

Calculated properties using top-level properties

Let’s start with an example JSON structure:

{
   "order": {
      "items": [
         {
            "name": "Keyboard",
            "quantity": 10,
            "price": 80
         },
         {
            "name": "Printer",
            "quantity": 5,
            "price": 250
         }
      ],

      "status": "Active"
   }
}

Suppose we want to clone order.items into a new calculated property called items. We can do this using the following syntax:

{{items = order.items}}

This syntax will create a new items property on top level of our JSON object and clone objects from order.items:

{
   "items": [
         {
            "name": "Keyboard",
            "quantity": 10,
            "price": 80
         },
         {
            "name": "Printer",
            "quantity": 5,
            "price": 250
         }
   ],

   "order": {
      "items": [
         {
            "name": "Keyboard",
            "quantity": 10,
            "price": 80
         },
         {
            "name": "Printer",
            "quantity": 5,
            "price": 250
         }
      ],

      "status": "Active"
   }
}

Now we can make various modifications to this new property we created and apply aggregate functions like count, sum, avg, etc. We can also use it in the final document by adding the following token: {{items}}

We can also add properties directly to our order object. For example we want to add a new property called isValid to the order object, which will check if the order is valid based on the number of items and status. We could use the following syntax:

{{order.isValid = order.items|count() > 0 and order.status == "Active"}}

Here, we count how many items are in order.items and check if there are more than 0 items and if the order status is “Active.” If both conditions are true, isValid will return true; otherwise, it will return false.

You can also write this syntax in different ways:

  • {{order.isValid = Count(order.items) > 0 and order.status == "Active"}}

  • {{orders.isValid = Count(order.items) > 0 and @value.1 == "Active"}}

In these examples, we work with properties at the top level. However, sometimes you may need to refer to properties at the current level, like an item within a collection. For instance:

{{order.items.total = @value.quantity * @value.amount)}}

The @value refers to the parent object of the calculated property.

The same logic applies when working with nested single objects:

{{order.isValid = @value.items|count > 0 and @value.status == "Active"}}

Result:

{
   "order": {
      "items": [
         {
            "name": "Keyboard",
            "quantity": 10,
            "price": 80
         },
         {
            "name": "Printer",
            "quantity": 5,
            "price": 250
         }
      ],

      "status": "Active",
      "isValid": true
   }
}

Calculated properties inside other calculated properties

When defining calculated properties, it’s important to set them up in the order they appear in the template. This allows you to use earlier calculated properties in later ones. For example:

{{order.items.total = @value.quantity * @value.price}}

You can then sum the totals like this:

{{order.total = order.items|sum(total)}}

Alternatively:

{{order.total = sum(order.items, total)}}

These lines will calculate totals appropriately based on previously defined properties.

Result:

{
   "order": {
      "items": [
         {
            "name": "Keyboard",
            "quantity": 10,
            "price": 80,
            "total": 800
         },
         {
            "name": "Printer",
            "quantity": 5,
            "price": 250,
            "total": 1250
         }
      ],

      "status": "Active",
      "total": 2050
   }
}

Calculated properties inside multiple nested collections

Now, let’s make the scenario a bit more complex by introducing a customers object in our JSON.

{
   "order": {
      "customers": [
         {
            "name": "John Doe",
            "items": [
               {
                  "name": "Keyboard",
                  "quantity": 10,
                  "price": 80
               },
               {
                  "name": "Printer",
                  "quantity": 5,
                  "price": 250
               }
            ]
         },
         {
            "name": "Jane Doe",
            "items": [
               {
                  "name": "Desk",
                  "quantity": 2,
                  "price": 600
               },
               {
                  "name": "Shredder",
                  "quantity": 3,
                  "price": 200
               }
            ]
         }
      ],

      "status": "Active"
   }
}

When dealing with nested collections, it’s important to reference the @value object to define the calculation context. In this case, each customer’s total will be calculated separately:

{{order.customers.items.total = @value.quantity * @value.price}}

You can then sum the total for each customer:

{{order.customers.total = @value.items|sum(total)}}

Result:

{
   "order": {
      "customers": [
         {
            "name": "John Doe",
            "items": [
               {
                  "name": "Keyboard",
                  "quantity": 10,
                  "price": 80,
                  "total": 800
               },
               {
                  "name": "Printer",
                  "quantity": 5,
                  "price": 250,
                  "total": 1250
               }
            ],

            "total": 2050
         },
         {
            "name": "Jane Doe",
            "items": [
               {
                  "name": "Desk",
                  "quantity": 2,
                  "price": 600,
                  "total": 1200
               },
               {
                  "name": "Shredder",
                  "quantity": 3,
                  "price": 200,
                  "total": 600
               }
            ],

            "total": 1800
         }
      ],

      "status": "Active"
   }
}