You are currently offline, serving cached version

Angular parser

The angular-parser makes creating complex templates easier. It allows for example:

{user.name}

To access the nested name property in the following data:

{
    user: {
        name: "John";
    }
}

You can also use +, -, *, /, >, <, ==, !=, <=, >= operators.

Setup

First install angular-expressions which is required to make the parser work :

npm install --save angular-expressions

Then use following code :

const expressionParser = require("docxtemplater/expressions.js");
new Docxtemplater(zip, { parser: expressionParser });

If running in IE 11 or if you get the error Proxy is not defined, you can use the following parser instead :

const expressionParser = require("docxtemplater/expressions-ie11.js");
new Docxtemplater(zip, { parser: expressionParser });

This require call works only with docxtemplater 3.32.3. If you have to use an older version of docxtemplater, copy this code

Conditions

With the angularParser option set, you can also use conditions:

{#users.length>1}
There are multiple users
{/}

{#userName == "John"}
Hello John, welcome back
{/}

The first conditional will render the section only if there are 2 users or more.

The second conditional will render the section only if the userName is the string "John".

It handles the following operators :

  • AND a && b
  • OR a || b
  • ADDITION a + b
  • SUBSTRACTION a - b
  • MULTIPLICATION a * b
  • MODULO a % b
  • DIVISION a / b
  • TERNARIES a ? b : c
  • ASSIGNMENTS a = 1
  • EQUALITY/INEQUALITY a == 1, a != 1
  • RELATIONAL a > 1, a < 1, a >= 1, a <= 1
  • OPERATOR PRECEDENCE with parenthesis (a && b) || c
  • EXPONENTIAL NUMBERS : 12e3 => returns 12000

For example, it is possible to write the following template:

{#myType === "users"}
There are {users.length} users.
{#cond1 || cond2}
Paragraph 1
{/}
{#cond2 && cond3}
Paragraph 2
{/}
{#cond4 ? users : usersWithAdminRights}
Paragraph 3
{/}
{/}

Filters

With filters, it is possible to write the following template to have the resulting string be uppercased:

{user.name | upper}
const expressionParser = require("docxtemplater/expressions.js");
expressionParser.filters.upper = function (input) {
    // Make sure that if your input is undefined, your
    // output will be undefined as well and will not
    // throw an error
    if (!input) return input;
    return input.toUpperCase();
};
new Docxtemplater(zip, { parser: expressionParser });

More complex filters are possible, for example, if you would like to list the names of all active users. If your data is the following:

{
    users: [
        {
            name: "John",
            age: 15,
        },
        {
            name: "Mary",
            age: 26,
        }
    ],
}

You could show the list of users that are older than 18, by writing the following code:

const expressionParser = require("docxtemplater/expressions.js");
expressionParser.filters.olderThan = function (users, minAge) {
    // Make sure that if your input is undefined, your
    // output will be undefined as well and will not
    // throw an error
    if (!users) return users;
    return users.filter(function (user) {
        return user.age >= minAge;
    });
};
new Docxtemplater(zip, { parser: expressionParser });

And in your template,

The allowed users are:

{#users | olderThan:15}
{name} - {age} years old
{/}

There are some interesting use cases for filters

Data filtering

You can write some generic data filters using angular expressions inside the filter itself.

{
    users: [
        {
            name: "John",
            age: 10,
        },
        {
            name: "Mary",
            age: 20,
        },
    ];
}
{#users | where:'age > 15'}
Hello {name}
{/}

The argument inside the where filter can be any other angular expression, with ||, &&, etc

The code for this filter is extremely terse, and gives a lot of possibilities:

const expressionParser = require("docxtemplater/expressions.js");
expressionParser.filters.where = function (input, query) {
    return input.filter(function (item) {
        return expressions.compile(query)(item);
    });
};
new Docxtemplater(zip, { parser: expressionParser });

Data sorting

If your data is the following:

{
    "items": [
        {
            "name": "Acme Computer",
            "price": 1000
        },
        {
            "name": "USB storage",
            "price": 15
        },
        {
            "name": "Mouse & Keyboard",
            "price": 150
        }
    ]
}

You might want to sort the items by price (ascending).

You could do that again with a filter, like this:

{#items | sortBy:'price'}
{name} for a price of {price} €
{/}

The code for this filter is:

const { sortBy } = require("lodash");
expressionParser.filters.sortBy = function (input, ...fields) {
    // In our example fields is ["price"]

    // Make sure that if your input is undefined, your
    // output will be undefined as well and will not
    // throw an error
    if (!input) return input;
    return sortBy(input, fields);
};

Multi level loops

It is possible to have multilevel loops (also called nested loops), if your data is the following :

{
    "companies": [
        {
            "name": "EvilCorp",
            "users": [
                {
                    "firstName": "John"
                },
                {
                    "firstName": "Mary"
                }
            ]
        },
        {
            "name": "HeavenCorp",
            "users": [
                {
                    "firstName": "Jack"
                },
                {
                    "firstName": "Bonnie"
                }
            ]
        }
    ]
}

You can loop on companies.users directly using following tag and filter :

{#companies | loop:'users'}
    {firstName}
{/}
expressionParser.filters.loop = function (input, ...keys) {
    const result = input.reduce(function (result, item) {
        (item[keys[0]] || []).forEach(function (subitem) {
            result.push({ ...item, ...subitem });
        });
        return result;
    }, []);
    if (keys.length === 1) {
        return result;
    } else {
        keys.shift();
        return expressionParser.filters.loop(result, ...keys);
    }
};

With this code, you can also have more nesting if needed :

{#companies | loop:"users":"tasks"}{/}

Data aggregation

If your data is the following:

{
    "items": [
        {
            "name": "Acme Computer",
            "price": 1000
        },
        {
            "name": "Mouse & Keyboard",
            "price": 150
        }
    ]
}

And you would like to show the total price, you can write in your template:

{#items}
{name} for a price of {price} €
{/}
Total Price of your purchase: {items | sumby:'price'}€

The sumby is a filter that you can write like this:

expressionParser.filters.sumby = function (input, field) {
    // In our example field is the string "price"

    // Make sure that if your input is undefined, your
    // output will be undefined as well and will not
    // throw an error
    if (!input) return input;

    return input.reduce(function (sum, object) {
        return sum + object[field];
    }, 0);
};

Data formatting

This example is to format numbers in the format: "150.00" (2 digits of precision) If your data is the following:

{
    "items": [
        {
            "name": "Acme Computer",
            "price": 1000
        },
        {
            "name": "Mouse & Keyboard",
            "price": 150
        }
    ]
}

And you would like to show the price with two digits of precision, you can write in your template:

{#items}
{name} for a price of {price | toFixed:2} €
{/}

The toFixed is an angular filter that you can write like this:

expressionParser.filters.toFixed = function (input, precision) {
    // In our example precision is the integer 2

    // Make sure that if your input is undefined, your
    // output will be undefined as well and will not
    // throw an error
    if (!input) return input;

    return input.toFixed(precision);
};

Assignments

With the angular expression option, it is possible to assign a value to a variable directly from your template.

For example, in your template, write:

xxx
{full_name = first_name + last_name}
yyy

This will show in your output :

xxx

yyy

To drop the new line, use a raw tag instead, starting with a @ :

xxx
{@full_name = first_name + last_name}
yyy

Output :

xxx
yyy

Retrieving $index as part of an expression

One might need to have a condition on the $index when inside a loop.

For example, if you have two arrays of the same length and you want to loop over both of them at the same time:

{
    names: ["John", "Mary"],
    ages: [15, 26]
}
{#names}
{#$index == 0}First item !{/}
{names[$index]}
{ages[$index]}
{/names}
Talk with sales