Word templates

Template functions

Ten helpers are available in every command without an import.

NamePurpose
_ Lodash. Group and sort lists, create ranges
pluralize Language-dependent singular and plural forms
imask Format numbers and strings
combine Join values, with text before and after
img Load and render an image
fdate Format an ISO date
fcheck Switch field as text
ffmt Output a formatted field value (output formatter)
ui View settings (not a function, an object)
deco Carry field annotations over into the report

_: Lodash

The complete Lodash . Often used: _.range, _.groupBy, _.sortBy, _.uniq.

{# FOR i IN _.range(0, eingang.length) #} … {# END-FOR i #}
{#! gruppen = _.groupBy(objects, o => o[`RaumNr`] ?? `ohne Raum`) #}

pluralize(language, value, forms)

Chooses the form that fits the number. Registered languages: de and en.

{#$o[`Anzahl Teile`]#} {#pluralize(`de`, $o[`Anzahl Teile`], { one: `Teil`, other: `Teile` })#}
{#objects.length#} {#pluralize(`de`, objects.length, { one: `Objekt`, other: `Objekte` })#}

imask(value, fieldDefinition?, customMask?)

Formats numbers and strings according to imask.js . For an empty value it returns null: a bare call then renders empty, never null or NaN in the document, and imask(…) ?? `-` applies.

The configuration is resolved in three levels:

LevelSourceRemark
1Custom mask (third parameter)Always wins; it is laid over the field mask
2Field definition from f[`Feld`]The mask from the field configuration
3Number format of the person exportingFrom the view settings, two decimal places. If the field has a unit but no mask, the unit is appended
{#imask($o[`Gewicht`], f[`Gewicht`]) ?? `-`#}
{#imask($o[`Breite`], false, { mask: Number, scale: 2, thousandsSeparator: `.`, radix: `,` })#}

Level 3 depends on the user who exports. For reproducible templates use level 1 or 2. A faulty field mask never breaks the export; it falls back to level 3.

combine(value, options?)

Wraps a string or joins an array. For empty input it returns def.

OptionDefaultMeaning
pre''Text before
post''Text after
def''Result for empty input
sep', 'Separator between array elements
filterEmptyfalseLeave out empty elements
{#combine($o[`Inventarbemerkung`], { pre: `(`, post: `)`, def: `k. A.` })#}
{#combine([$o[`Künstler`], $o[`Datierung`]], { sep: `, `, filterEmpty: true })#}
{#combine([parseFloat($o[`Höhe`]), parseFloat($o[`Breite`])].filter(Boolean).join(` x `), { post: ` cm` })#}

The benefit: brackets and units disappear when there is no value, instead of a lonely ( ) or cm in the document.

combine handles only strings. A number gives def, even inside an array. That is why the third example does join first and then combine. To wrap a single number, write combine(String(value), …). The option is called def, not default: an unknown option is silently ignored.

img(file, config)

Used in IMAGE commands; it produces an image for Word. file is a file object from $f or files, an image field value ($o[`Titelbild`]) or a file ID as a string.

OptionMeaning
width, heightBounding box in cm; the image is fitted with its aspect ratio kept. A single value acts as a square box; without either, 6 × 6 cm applies
altAlternative text
captionCaption
rotationRotation in degrees, only if render.rotation is not switched off
render.cropApply the crop set in Moliri. Default true
render.overlayDraw in markings. Default true
render.rotationTake rotation into account. Default true

So the default is the edited image, not the original:

{# IMAGE img($file, { width: 6 }) #}
{# IMAGE img($file, { width: 6, render: { crop: false, overlay: false } }) #}
{#IMAGE img(($o[`Titelbild`] ?? null), { width: 8, height: 6 })#}

The ?? null on an image field makes sure that objects without an image get a placeholder instead of an error. If loading fails, a placeholder image appears as well. Moliri loads identical image requests only once per export run.

Resolution is not a template property

The DPI are set on export, with a default of 220. Word itself compresses embedded images back to about 220 DPI when saving, unless automatic image compression is switched off. For print templates it is worth switching it off in Word after opening the file for the first time.

Filter files before img()

$f contains all files of the object, including PDFs and documents. Files that cannot be shown directly get placeholders or generated previews, so a filter is not strictly necessary. If you want images only, filter:

{# FOR file IN $f #}
{# IF [`image`, `image_other`, `svg`].includes($file.kind) #}
{# IMAGE img($file, { width: 6 }) #}
{# END-IF #}
{# END-FOR file #}

The valid values for kind are listed under Supported formats .

fdate(value, format?)

Formats ISO date values, including those with a time. The format is in the command, so the result is the same for all users.

FormatResult for 2026-03-05
TT.MM.JJJJ (default)05.03.2026
JJJJ2026
MM.JJJJ03.2026
T. Monat JJJJ5. März 2026
JJJJ-MM-TT2026-03-05
TT/MM/JJJJ05/03/2026
MM/TT/JJJJ03/05/2026
autoDate format from the view settings of the person exporting

Behaviour at the edges:

  • Empty values and 0000-00-00 give null. That renders empty, and ?? applies.
  • Incomplete dates are shortened: 2025-00-00 becomes 2025, 2025-03-00 becomes 03.2025 or März 2025.
  • Values that do not start with an ISO date (free text such as um 1900, “around 1900”) come back unchanged.
  • An unknown format counts as TT.MM.JJJJ.
{#fdate($o[`Eingangsdatum`])#} bis {#fdate($o[`Rückgabedatum`])#}
{#fdate($o[`Datierung`], `JJJJ`) ?? `o. J.`#}
{#fdate(p.start_date, `T. Monat JJJJ`)#}

fcheck(value, yes?, no?)

Outputs a switch field as text. An empty value counts as “no”, as does anything that looks like a negation: 0, nein, no, false, aus, off, ❌ and similar. Everything else counts as “yes”. This way fcheck also reads switches with custom stored values correctly.

Without further arguments, the labels come from the view settings of the person exporting:

{#fcheck($o[`Kurierbegleitung`])#}                → view settings
{#fcheck($o[`Kurierbegleitung`], `Ja`, `Nein`)#}  → fixed label
The default values are emoji

In the view settings, ✅ and ❌ are preset. In Word, how they look depends on the font of the paragraph. For print templates, Ja/Nein or X/- are the safe choice, that is the second form above. Only when there is no view setting does Moliri itself use Ja and Nein.

ffmt(record, key)

Outputs the value of a field as its output formatter formats it. The first parameter is the record, $o or the loop variable of a loop:

{#ffmt($o, `Maße`) ?? ``#}
{#ffmt($o, `Maße.Objekt`)#}
{#ffmt($o, `Leihgeber`) ?? `-`#}
{# FOR obj IN objects #}{#ffmt($obj, `Maße`)#}{# END-FOR obj #}

Without a formatted value it returns null, and ?? applies. Keys with an entry (.) and a variant (#), and the built-in formatting for each field type, are described under Output formatters in templates .

ui: view settings

Not a call, an object. Keys and the warning are under Template variables .

{#$o[`Datierung`] ?? ui.null_replacement#}
{#fdate($o[`Eingangsdatum`], `auto`)#}

deco(annotations, config)

Carries field annotations over into the report: a field’s colour marking as the background of the building block, comment and source as a Word comment.

You set this up in the template editor, not by hand. In the panel Moliri · Insert & edit, every field you insert and every building block you click has the section Additional details with the rows Colour, Comment and Source. There you choose which field provides the colour and which fields contribute comments and sources. Moliri saves the choice on the building block’s content control and inserts the matching call itself on export, in loops with the annotations of each object.

The generated call looks like this:

{# deco($m, { color: `RegNr`, comments: [`RegNr`], sources: [] }) #}
KeyMeaning
colorField name whose colour marking appears as the background
commentsField names whose comments appear as a Word comment
sourcesField names whose source references appear as a Word comment

Without an annotation the call gives an empty string; no empty marking is created.

Written by hand, deco has no effect

The post-processing that sets colour and comments only runs for building blocks set up this way in the template editor. A deco call that sits in the template as plain text leaves an unreadable string of characters in the report.

JavaScript in the command

Besides the helpers, simple expressions work directly:

{# INS [$o[`Feld A`], $o[`Feld B`]].filter(Boolean).join(`, `) #}

For more involved logic, a preparatory EXEC step at the start of the document is easier to follow than nested one-liners. See Recipes .

Helpers and expressions run in isolation. They see only the helpers on this page and the data Moliri has loaded for the export. They cannot reach the network (fetch, WebSocket, outside scripts), nor the browser storage with the login; the check reports such a call as Not allowed: …. A run may take 60 seconds, not counting the time for loading images. The details are under Execution and errors .

Further reading