jsreport uses javascript templating engines to define report layout. You can bind input data, use loops, conditions or javascript helpers to dynamically build your content. Templating engines provide a way to define any custom report you like in a very fast, flexible, matured, and well-known way.
{{#each items}}
<tr class="item">
<td>
$ {{calculateVAT price}}
</td>
</tr>
{{/each}}
jsreport default installation includes two templating engines. It's handlebars and jsrender. Both are very popular with a ton of sources and examples on the internet. The most common start is with handlebars, and it's also recommended because some recipes currently support just handlebars. Additionally you can install custom templating engines EJS and PUG or even provide your own custom engine.
The goal of the templating engine is to dynamically build report source, typically html, based on the input data. The main components participating in the engine evaluation are
You can see on the following screenshot where these components can be found in the jsreport studio.
The syntax differs for every templating engine, but the following chapters will describe the general concept that is the same for every engine.
The common component for every templating engine is a custom helper function. These functions are placed at the bottom panel of the template and their main goal is to customize the evaluation using javascript. Such helpers can help with calculation or even dynamically produce a block of html. The call of a helper function is invoked using specific templating engine syntax and the return value of the function is inserted at the place where it was called. The functions are evaluated in jsreport nodejs sandbox and may include any javascript.
The helper functions can also use third-party modules.
First, you need to make sure the third-party module is installed and available.
npm i moment --save
The next step is enabling access to the local files and modules in the configuration.
{
"trustUserCode": true
}
Alternatively, you can also use config sandbox.allowedModules=["moment"]
and name the modules you allow.
Then you can use the custom module inside your helpers in the following way.
const moment = require('moment')
function formatNow() {
return moment().format('DD.MM.YYYY')
}
Alternatively you can also let jsreport install the npm module for you. See the npm extension for the details.
The helper functions can be asynchronous and return promises. This can be handy to perform external calls or invoke modules that don't have synchronous API.
You can, for example, resize an image before putting it to the pdf and produce smaller pdfs.
const Jimp = require('jimp')
async function resize (url) {
const newImage = await Jimp.read(url)
newImage.resize(50, 50)
const newImageDataUri = await newImage.getBase64Async(Jimp.AUTO)
return newImageDataUri
}
The helpers section can be also asynchronous at the top level. This can be beneficial when a custom library needs an asynchronous initialization code.
const someLibrary = require('someLibrary')
await someLibrary.init()
function myHelper() {
return someLibrary.foo()
}
The async helper result can be explicitely awaited. Like in this case, the helper parameter is result of an async helper call.
{{myHelper (asset "config.json")}}
Then the parameter value needs to be awaited using the following call.
const jsreport = require('jsreport-proxy')
async function myHelper(configAsync) {
const configStr = await jsreport.templatingEngines.waitForAsyncHelper(configAsync)
return JSON.parse(configStr ).propA
}
To await all async helper calls and run some final code, use the following
jsreport.templatingEngines.waitForAsyncHelpers()
.then(() => console.log('all async helpers processed'))
Note the async code in helpers shouldn't be used to load or manipulate the input data. Such tasks should be done using jsreport custom scripts.
Many extensions provides system helpers you may use. You can refer to the particular extension's docummentation for the details. The core provides the following system helpers:
Import an installed node.js module to the template content.
This works for all engines, using, for example, handlebars an example can look like this.
<script>{{module "moment"}}</script>
<script>console.log(moment())</script>
Convert input into javascript object. This is usefull when you need to access input data inside <script>
.
<script>
const reportData = {{{toJS this}}}
...
</script>
Since the node.js 14 you can use the ECMA script Intl standard to format numbers, dates, or use plural rules.
You can, for example, define a helper for formatting money and then use it based on your templating engine.
function formatPrice(number, culture, currency) {
return new Intl.NumberFormat(culture, { style: 'currency', currency }).format(number)
}
{{formatPrice 12.5 'cs-CZ' 'EUR'}}
See the Intl documentation for more details.