FreeMarker Document Templates for TrueContext Data

Apache FreeMarker™ is a free template engine owned by the Apache Software Foundation. We’ve incorporated FreeMarker into some of our TrueContext features so that you can dynamically generate text from a submitted record. For example, you can generate a text file or HTTP Request Body. You can also use FreeMarker to create custom layouts for PDF, Word, and HTML documents.

This topic describes how to reference TrueContext data in a FreeMarker template.

Tip:This topic is designed for TrueContext users who have basic scripting and programming skills.

Available on the Advanced and Enterprise tiers:

Essentials
Advanced
Enterprise

Contents

What you need to know to use FreeMarker with TrueContext

When you need the full power of a programming language to build templates with sophisticated loop and conditional structures, the Apache FreeMarker Template Language (FTL) can achieve results that other template types can’t. Here’s what you should know before you get started.

Basic scripting and programming concepts

Understanding basic programming concepts—such as variables, if-else conditions, loops, and data structures—will help you understand the FreeMarker Template Language (FTL).

If you want to generate custom PDF, Word, or HTML output, you should know HTML and CSS syntax and coding conventions.

You can find basic programming reference guides and tutorials online.

How to use FreeMarker

The FreeMarker Template Language (FTL) includes a set of data-handling functions known as built-ins and directives. Directives are FTL commands that tell the template engine how to reference and format data. Directives can loop through sequences of data, conditionally show or hide content, and handle errors, for example.

FTL has a specific syntax for the expressions in a template. To learn more about FTL:

The TrueContext data model

To give customers access to the full details of a submitted form, TrueContext creates JSON and XML representations of the data record. TrueContext provides the data structure as a JSON-formatted “tree”. When you build a FreeMarker template, you base the template on the TrueContext data structure.

Although the JSON tree structure is consistent across forms, the actual JSON or XML for a record depends on the form design and the submitted data.

Tip:The data model might include properties for features that aren’t available on your form or tier. Check your template to make sure that you only reference properties that are available and set up on your form.

How to handle incomplete data sets

The template must handle optional or incomplete data, where values might not be submitted in all cases. It must also account for Data Destination filtering that excludes data from the record, as well as dispatched records that don’t have all data prefilled.

Info:The section Handle optional or incomplete data provides basic syntax that you can use to check for empty or null values.

The data set that you want to include in your output document

The template engine generates textual output based on templates and input data. For example, you can dynamically generate text for a:

  • PDF file, based on HTML and CSS, presented as a detailed report of work performed. You need to know which details from submitted records you want to include in the report.
  • JSON, XML, or CSV message that contains data from submitted records to send to a customer or partner integration point. You need to know which data points (answers) to get and the format that’s required for the integration point.

Syntax quick reference

Record metadata

To reference metadata, use the dataRecord node in your expressions.

Syntax Example
${dataRecord.key}

${dataRecord.identifier}

Returns the unique record number that’s assigned by the TrueContext system.

${dataRecord.key.property}

Note:For dataRecord keys that have multiple properties, you must specify a property.

${dataRecord.user.username}

${dataRecord.user.identifier}

Returns the username and numeric identifier, but not the display name.

Note:The Preview option uses a full set of sample answer data to ensure that your template works in all cases.

For the dataRecord node, however, your template must include the null operator in all expressions. This ensures that the preview works as expected. For example, if your template includes:

${dataRecord.identifier}

Add the null operator to the end of the expression:

${dataRecord.identifier!""}

Answer values

The TrueContext data model provides a top-level answers node to simplify how you reference data in a Regular or Side-by-Side section. The answers node flattens the form structure and uses the question unique IDs (labels) as keys in the key:value pairs. The keys are based on the questions and are specific to each form.

The answers node syntax is the easiest way to reference data in a Regular or Side-by-Side section.

Tip:Some question types don’t show all properties in the answers node, such as the Question Text. You can reference those properties by specifying the full path, for example:

${dataRecord.pages.pageID.sections.sectionID.answers.uniqueID.question}

Info:If you want to reference answers in a Repeatable SectionClosed A Repeatable Section is a subform that contains a set of related questions. The data captured is “repeating”, because the field user can complete the same subform more than once, which creates multiple entries., you must specify the full path to the answer values. The section Answers in a Repeatable Section describes how to use the FreeMarker built-in #list directive to loop over the values.

Answers with a single value

Syntax Example

${answers.uniqueID[n]}

Note:Because our data model uses arrays for all answer values, you must include the array index in brackets ( [ ] ). The array structure allows for questions that have more than one answer.

${answers.Inspector[0]}

Returns the value of the Inspector field.

[0] gets the first or only value in the array.

Answers with multiple values

Some question types—such as Multiselect or attachment-based questions—can have multiple values. Use the #list directive to get all of the values.

Syntax Example
<#list answers.uniqueID as variable>
${variable}
</#list>
<#list answers.MultipleAnswers as values>
${values}
</#list>

Returns a list of submitted values.

Answers with multiple properties

Some question types have multiple properties, such as Geo Location.

Syntax Examples

${answers.uniqueID[n].property}

${answers.locationOfIncident[0].address}

Returns the submitted address.

${answers.locationOfIncident[0].coordinates.latitude}

Returns the latitude in decimal degrees.

Images and other binary content

You can reference the following properties for Image, Sketch Pad, File Upload, Signature, and Document Editor attachments.

  • identifier—The unique attachment identifier in the TrueContext system.
  • filename—The filename and extension assigned by the TrueContext system.
  • contentType—The file type, such as image/jpeg or application/pdf.
  • bytes—The actual content of the attachment, Base64-encoded.
Syntax Examples

${answers.uniqueID[n].property}

${answers.UploadImage[0].identifier}

Returns:

d9257339-aa49-4267-98a9-17cc7aaf56ee

${answers.UploadImage[0].filename}

Returns:

UploadImage_1.jpg

${answers.UploadImage[0].contentType}

Returns:

image/jpeg

${answers.UploadImage[0].bytes}

Returns the Base64-encoded content:

iVBORw0KGgoAAAANSUhEUgAAAh4AAAIQCAYAAADOyWc…

Answers in a Repeatable Section

The TrueContext data model treats each “row” in a Repeatable Section as a subform. Each subform has its own pages, sections, and answers nodes.

To reference answers in a Repeatable Section:

  • Use the FreeMarker built-in #list directive to loop over the repeatable section.
  • Set up expressions to “walk the tree” of the data model hierarchy. The path depends on the node format option that you choose.

Tip:We recommend that you select All Labels as Node Names when you set up your document. This makes it easier and more precise to “walk the tree” of our data model. The following examples are based on this setting.

Syntax
<#list dataRecord.pages.pageID.sections.sectionID.rows as row>
  <#list row.pages.RowPageID.sections.RowSectionID.answers.uniqueID.values as value>
    ${value}
  </#list>
</#list>
Example
<#list dataRecord.pages.NewPage.sections.IncidentCauses.rows as row>
  <#list row.pages.IncidentCauses.sections.IncidentCauses1.answers.Cause.values as selection>
    <#list row.pages.IncidentCauses.sections.IncidentCauses1.answers.Details.values as detail>
${selection} - ${detail}
    </#list>
  </#list>
</#list>

Returns, for example:

Equipment failure - Belt
Power outage - Storm

Date, Time, and Date/Time answers

Basic Date/Time syntax

The syntax you use to reference data in a Date/Time Selector or Date/Time Stamp field depends on the data type, as described in the following table.

Info:The examples show the output in the default ISO Date and Time formats. You can change the format using either FreeMarker or DRELClosed Data Reference Expression Language (DREL) is used to get form data and metadata and add it to a string, such as dates, usernames, or answers to questions in forms..

Data typeClosed Data types describe the format in which a user answers a question. For example, a user might enter a name (Text) in one question, and a dollar amount (Currency) in another. A data type also supports validation (making sure that mobile users enter a valid email address format, for example). Syntax Example
Date

${answers.uniqueID[n]}

${answers.DateOnlySelector[0]}

Returns, for example:

2024-05-27

Time

${answers.uniqueID[n].provided.time}

${answers.TimeOnlySelector[0].provided.time}

Returns, for example:

14:00:00+01:00

The default time format uses the 24-hour clock, and ±HH:mm indicates the UTC offset.

${answers.uniqueID[n].provided.zone}

${answers.TimeOnlySelector[0].provided.zone}

Returns, for example:

Europe/London

The default format is as per the list of tz database time zones.

${answers.uniqueID[n].shifted}

${answers.TimeOnlySelector[0].shifted}

Returns, for example:

2024-06-04T23:07:00+02:00

This is the provided time shifted to the time zone selected as the Document Time Zone Source.

Date/Time

${answers.uniqueID[n].provided.time}

${answers.DateTime[0].provided.time}

Returns, for example:

2024-05-27T13:15:05+01:00

${answers.uniqueID[n].provided.zone}

${answers.DateTime[0].provided.zone}

Returns, for example:

Europe/London

The default format is as per the list of tz database time zones.

${answers.uniqueID[n].shifted}

${answers.DateTime[0].shifted}

Returns, for example:

2024-06-04T23:07:00+02:00

This is the provided time shifted to the time zone selected as the Document Time Zone Source.

Info:The system returns the date and time in ISO format. The section Date/Time formats describes how you can change the default format.

Date/Time formats

Dates, times, and date/times default to the following formats:

  • Date—yyyy-MM-dd
  • Time—HH:mm:ssXXX
  • Date/Time—yyyy-MM-dd'T'HH:mm:ssXXX

You can change date and time formats using either FreeMarker built-ins or DREL expressions.

Note:If, by mistake, you use a date format for a Time data type or a time format for a Date data type, the system returns a default value. Check that your output Document values are as expected.

FreeMarker

TrueContext returns dates and times as strings. You use the date, time, or datetime built-ins to parse the strings into a date, time, or datetime object. The .xs format specifier converts the value into the XML Schema date/time format (ISO 8601).

To get the format that you want, use the string built-in to format the date, time, or datetime object.

Example Result
${answers.dateTimeIncident[0].provided.time?datetime.xs?string("MM-dd-yyyy hh:mm a z")}

06-04-2024 02:07 PM PDT

${answers.DateOnlySelector[0]?date.xs?string("MM-dd-yyyy")}

06-04-2024

${answers.TimeOnlySelect[0].provided.time?time.xs?string("hh:mm a")}

02:07 PM PDT

The time zone resolves to the Document Time Zone Source when you use FreeMarker built-ins to format a date, time, or datetime value. In the previous examples, the time zone is displayed as PDT, which is the time zone set for the team (America/Vancouver).

You can set different time zones within your template using the #setting directive, for example:

<#setting time_zone="America/New_York">

This setting only affects dates, times, and datetimes that you format using FreeMarker built-ins. Each time zone setting applies to the data that follows until you reset the time zone with another #setting directive.

DREL

You can use the DREL method to change the format of a date, time, or datetime.

Info:The topic DREL Complete Reference List contains detailed information about DREL expressions.

Example Result
<#-- For the data record (not specific answers) -->
${drel("%t[MM-dd-yyyy hh:mm a z]")}
<#-- For a specific answer -->
${drel("%a[dateTimeIncident][MM-dd-yyyy hh:mm a zzzz]")}

06-04-2024 10:09 PM BST

06-04-2024 10:07 PM British Summer Time

Time zone overrides

FreeMarker time zone setting

You can set different time zones within your template using the #setting directive, for example:

<#setting time_zone="America/New_York">

This setting only affects dates, times, and datetimes that you format using FreeMarker built-ins. Each time zone setting applies to the data that follows until you reset the time zone with another #setting directive.

Example Result
<#setting time_zone="America/New_York">
${answers.dateTimeIncident[0].provided.time?datetime.xs?string("MM-dd-yyyy hh:mm a z")}

06-04-2024 05:07 PM EDT

DREL time zone settings

You can use the DREL method to set the time zone to a specific value or to one of the following values:

  • DataRecordTZ: Time zone on the user’s device.
  • TeamTZ: Time zone configured at the team level.
  • Region/City: A specific time zone. You can use any of the names in the list of tz database time zones.
Example Result
${drel("%a[dateTimeIncident][MM-dd-yyyy hh:mm a z][America/Toronto]")}

06-04-2024 05:07 PM EDT

${drel("%a[dateTimeIncident][MM-dd-yyyy hh:mm a z][TeamTZ]")}

06-04-2024 02:07 PM PDT

Shifted time

The shifted time depends on the Document Time Zone Source, as described in the following table. The examples show the UTC offset for each time zone.

Note:The device time zone refers to the device setting, not the user profile locale settings in the TrueContext Mobile App account details.

If the Document Time Zone Source is set to… Then the shifted time is in the… Examples
Form Submission Device time zone, which is the provided time. In this case, the provided time and the shifted time are the same.

Date Time of Incident - provided time: 2024-06-04T22:07:00+01:00

Date Time of Incident - shifted: 2024-06-04T22:07:00+01:00

Team Preference Setting

Team time zone

Date Time of Incident - provided time: 2024-06-04T22:07:00+01:00

Date Time of Incident - shifted: 2024-06-04T14:07:00-07:00

Custom The custom time zone that you specify

Date Time of Incident - provided time: 2024-06-04T22:07:00+01:00

Date Time of Incident - shifted: 2024-06-04T23:07:00+02:00

Pages and sections

You can use the FreeMarker built-in #if directive to conditionally skip a section of the template. This provides a way to show or hide content, for example.

To reference specific pages and sections, set up expressions to “walk the tree” of the data model hierarchy. The path depends on the node format option that you choose.

Tip:We recommend that you select All Labels as Node Names when you set up your document. This makes it easier and more precise to “walk the tree” of our data model. The following examples are based on this setting.

Syntax Example
<#if answers.uniqueID?has_content>
${dataRecord.pages.pageID.sections.sectionID.property}
<#list dataRecord.pages.pageID.sections.sectionID.rows as row>
  <#list row.pages.RowPageID.sections.RowSectionID.answers.uniqueID.values as value>
    ${value}
  </#list>
</#list>
</#if>
<#if answers.KnownCause?has_content>
${dataRecord.pages.NewPage.sections.IncidentCauses.name}
<#list dataRecord.pages.NewPage.sections.IncidentCauses.rows as row>
  <#list row.pages.IncidentCauses.sections.IncidentCauses1.answers.Cause.values as selection>
  <#list row.pages.IncidentCauses.sections.IncidentCauses1.answers.Details.values as detail>
${selection} - ${detail}
    </#list>
  </#list>
</#list>
</#if>

Returns…

  • If answered:

    Incident Causes

    Equipment failure - Worn belt

    Power outage - Storm

  • If not answered, doesn’t display the section name or the answers in that section.

drel( ) method

Data Reference Expression Language (DREL) is a TrueContext proprietary language used to reference data from a submitted record.

You can use the drel() method to get data and metadata from a submitted record.

Syntax Example

${drel(“%reference[property]”)}

${drel("Display valueClosed Options-based questions have a display value and a data (server) value. The display value is the answer shown in the Mobile App. The Form Designer can choose whether the display and data values are the same or different.: %a[EquipmentCondition][display]")}

Returns:

Display value: Good

This example returns the display value instead of the server value for this options-based question.

Tip:You can include static text in your DREL expressions. This example includes the static text Display value:

Info:The topic DREL Complete Reference List describes all the available DREL references and properties.

Handle optional or incomplete data

The template must handle optional or incomplete data, where values might not be submitted in all cases. It must also account for Data Destination filtering that excludes data from the record, as well as dispatched records that don’t have all data prefilled.

Note:If the template expects a value and there isn’t one in the submitted data, document generation might fail. We recommend that you check for empty or null values for all answers.

You can handle null or empty answers in one of two ways:

  • Use the ! null operator to check if the answer is null or empty.
    • If the answer is null or empty, the document displays the characters inside the quotation marks. To display nothing, don’t specify any text:

      ${(answers.uniqueID[n])!""}

    • If the answer has a value, the document displays the value.

  • Use the #if directive when you need to do more complex checks. For example, you might need to handle multiple conditions or include additional actions based on whether the value exists.
Note:The Preview option uses a full set of sample answer data to ensure that your template works in all cases.

For the dataRecord node, however, your template must include the null operator in all expressions. This ensures that the preview works as expected. For example, if your template includes:

${dataRecord.identifier}

Add the null operator to the end of the expression:

${dataRecord.identifier!""}

The following table provides some example syntax you can use to handle null or empty data.

Syntax Example

${(answers.uniqueID[n])!"text"}

${(answers.Inspector[0])!"Not answered"}

Returns, if answered:

LaStrade

Returns, if null or empty:

Not answered

<#if answers.uniqueID?has_content>
Text: ${answers.Inspector[0]}.
<#else>
Text
</#if>
<#if answers.Inspector?has_content && answers.SeniorTechnician?has_content>
    The names of the inspectors are ${answers.Inspector[0]} and ${answers.SeniorTechnician[0]}.
<#elseif answers.Inspector?size == 0 && answers.SeniorTechnician?has_content>
    The name of the senior technician is ${answers.SeniorTechnician[0]}.
<#elseif answers.Inspector?has_content && answers.SeniorTechnician?size == 0>
    The name of the inspector is ${answers.Inspector[0]}.
<#else>
    No inspector information available.
</#if>

Returns…

  • If both questions are answered:

    The names of the inspectors are LaStrade and Watson.

  • If only Inspector is answered:

    The name of the inspector is LaStrade.

  • If only SeniorTechnician is answered:

    The name of the senior technician is Watson.

  • If neither question is answered:

    No inspector information available.

Note:TrueContext does not support the FreeMarker attempt/recover directive.

Escape special characters

Special characters or line breaks in the data could result in a document that’s not valid for your cloud system or endpoint. Follow the instructions in the topic Escape Special Characters and Line Breaks for Template-driven Text Documents.

Unsupported functions

For security reasons, the system prevents the use of the include and import functions in a TrueContext FreeMarker template. If the system detects those functions, it removes them from the template.

Components of the TrueContext data model

Record metadata

The dataRecord (root) node contains all of the data and metadata from the submitted record. The dataRecord node properties include the unique identifier, name, and dates submitted and processed, for example. Child nodes contain geo stamp (geotag) and device details.

Form and FormSpace information

The form child node has properties that can help you identify the Form DefinitionClosed A Form Definition is a template for an actual form or record that field users interact with. Using the Form Builder, you define the structure, layout, fields and data types, and logic of a form. A Form Definition also specifies resources available to users on their devices, such as Images and Documents, and mappings for data brought in from external sources. and version, as well as the FormSpaceClosed FormSpaces are where forms are stored and organized in the TrueContext Web Portal. A TrueContext Team may have multiple FormSpaces, depending on their needs. Admins can set FormSpace permissions to control which users have access to the forms in that FormSpace. it’s in.

User information

The user child node properties tell you who submitted, transferred, or edited the record.

The “answers” node

The JSON tree structure follows the structure of the Form Definition. There are nodes for pages, sections, and answers (fields).

The TrueContext data model provides a top-level answers node to simplify how you reference data in a Regular or Side-by-Side section. The answers node flattens the form structure and uses the field unique IDs (labels) as keys in the key:value pairs. The keys are based on the questions set up in the Form Definition and are specific to each form.

Note:The system strips out spaces from the unique IDs and truncates them if they’re longer than 19 characters. Use the keys exactly as shown in the data model, not as shown in the Form Builder.

JSON data structure that shows the "answers" node with "InspectionDate", "Inspector", and "Job" subvariables

The answers.uniqueID syntax is the easiest way to reference data in a Regular or Side-by-Side section.

Tip:Some question types don’t show all properties in the answers node, such as the Question Text. You can reference those properties by specifying the full path, for example:

${dataRecord.pages.pageID.sections.sectionID.answers.uniqueID.question}

Sections

Different types of sections have different JSON representations, as shown in the following table.

Section type JSON identifier Node structure
Regular Flow
"sections": [
                {
                    "type": "Flow",
                    "label": "QuestionsPush",
                    "name": "Questions to push from",
                    "answers": [
Side-by-Side QuestionTextColumn
"sections": [
                {
                    "type": "QuestionTextColumn",
                    "label": "Job Information",
                    "name": "Job Information",
                    "answers": [
Repeatable (or “Repeating”) Repeat
"sections": [
                {
                    "type": "Repeat",
                    "label": "Subform",
                    "name": "Subform",
                    "rows": [
                        {
                            "pages": [
                                {
                                    "label": "Subform",
                                    "name": "Subform",
                                    "sections": [
                                        {
                                            "type": "Flow",
                                            "label": "Subform 1",
                                            "name": "Subform",
                                            "answers": [

Node format options

When you set up your FreeMarker output document, you can choose how the nodes in the JSON tree are named and structured. This helps you align the data model with the requirements of your ecosystem.

Choose one of the following options:

  • Standard—This option provides the form elements in an array that you can loop over. Use this option when you want to apply generic logic to all elements of your form.

  • All Labels as Node Names—Uses the page, section, and answer unique IDs (labels) instead of the generic names.

    Tip:This option gives you precise labels you can use to reference specific nodes in the submitted record. Use this option when you want to place specific answers in specific places in your template output. The examples in this topic all use this setting.

  • Flat Answer List—Changes the hierarchical tree structure into a flat data structure (list). Use this option when you want to loop over answers and don’t need to reference the full form structure (pages, sections, or Repeatable Sections).

Build an FTL template

High-level process

Stage Description
Get the TrueContext data model

This gives you the structure that you need to build your template. You can change the naming of tree nodes as needed. There are two ways that you can get the data model:

  1. View Template Input to download a JSON file that shows the data model for a specific Form Definition. You can view the input when you create or edit a Document, or after you save it.

  2. Download a Standard JSON file for a submitted record. This shows you actual data that your template needs to handle.

    Tip:The structure of a JSON record can vary widely, even for a single form. The data submitted by field users depends on Conditional Logic, Answer ExceptionsClosed An Answer Exception is a form feature that displays color-coded feedback when a mobile user selects or enters an answer. Answer Exceptions typically show an inspection status (pass/fail) or prompt the user for additional information. Color-coding notifies the user of the Exception's severity., and optional questions, for example. To avoid errors, build a template that can handle the variability of submitted records.

Choose a document type and set up the basics

FreeMarker templates work with the following document types:

  • FreeMarker text document
  • PDF, Word, and HTML custom layouts

Document basics include the:

Configure the Document and add the FreeMarker template
  • For a FreeMarker text document, upload the FTL file.
  • For a custom PDF, Word, or HTML layout, build or paste the template in the Custom Document Editor, HTML tab.
Test your Document

There are a few different ways you can test your document:

  • Select Preview to generate a document that uses sample (not actual) data.

    Note:The Preview option uses a full set of sample answer data to ensure that your template works in all cases.

    For the dataRecord node, however, your template must include the null operator in all expressions. This ensures that the preview works as expected. For example, if your template includes:

    ${dataRecord.identifier}

    Add the null operator to the end of the expression:

    ${dataRecord.identifier!""}

  • Complete and submit a form, and then download the document.

    If the document contents aren’t what you expected, you can edit the template, upload it to the document, and then generate and download the document again.

    Info:You can save some time by dispatching the form by using the TrueContext REST API. You can set up a Request Body that you can reuse for testing. The topic Dispatch a Form Using the API with ReqBin provides an example of how to set up an API dispatch.

Steps to build an FTL template for a FreeMarker text document

  1. Open a text editor to start building your FreeMarker template.

  2. In TrueContext, go to Manage Forms > Documents > Create Document. In the Template-driven Text Documents section, select FreeMarker.

  3. On the Document Basics tab, enter the Name and Filename.

  4. On the Configuration tab, enter the document details.

    • FreeMarker templates work with the default settings for Data Node Format and Content Type. You don’t need to change these.
    • Data Node Format

      A FreeMarker template is based on the “tree” structure of our JSON Form Definitions. The Data Node Format setting changes the structure and naming of the page, section, and answer nodes.

      Choose one of the following options:

      • Standard—This option provides the form elements in an array that you can loop over. Use this option when you want to apply generic logic to all elements of your form.

      • All Labels as Node Names—Uses the page, section, and answer unique IDs (labels) instead of the generic names.

        Tip:This option gives you precise labels you can use to reference specific nodes in the submitted record. Use this option when you want to place specific answers in specific places in your template output. The examples in this topic all use this setting.

      • Flat Answer List—Changes the hierarchical tree structure into a flat data structure (list). Use this option when you want to loop over answers and don’t need to reference the full form structure (pages, sections, or Repeatable Sections).

    • File Extension

      Enter the extension for the output file you want to create. For example, enter txt or json. Make sure the extension works with your endpoint or integration system.

    • Content Type

      This specifies the kind of data contained in the output file. This helps to ensure that the correct application or method is used to view or process the data.

    • FreeMarker Template

      View Template Input to download a JSON file that shows the data model for a specific Form Definition. You can view the input when you create or edit a Document, or after you save it. In your text editor, use this data model as a reference to build your FTL code.

      Tip:You can start with a very simple template and build on it later. For example, start with a single expression, such as:

      ${dataRecord.identifier}

      Save the file as .ftl.

  5. Select Choose File, and then select your FTL file.

  6. (Optional) Preview the output. Are the results as expected?

    • If yes, go to step 7.
    • If no, you can fix the FTL file now or after you save the Document.
  7. Select Create to save the FreeMarker Document.

Get the TrueContext data model

  1. Go to the FreeMarker Document, and then select View Template Input. You can view the input when you create or edit the Document, or after you save it.

  2. Select a Target Form. This should be the form that you want to link to the FreeMarker document.

  3. Select Show Template Input.

    Result: The system generates and downloads a JSON file that contains sample data. Use this view of the tree structure to set up your FreeMarker template.

    Info:For examples of how to reference the data in your template, refer to the Syntax quick reference.

    Tip:The data model might include properties for features that aren’t available on your form or tier. Check your template to make sure that you only reference properties that are available and set up on your form.

    The following example also shows how to reference answers that have additional properties:

    Note:Test your template thoroughly with actual data from submitted forms.

    For security reasons, the system prevents the use of the include and import functions in a TrueContext FreeMarker template. If the system detects those functions, it removes them from the template.

Basic example of template and output

If the FreeMarker template contains this content:

Username: ${dataRecord.user.username!""}
ID: ${dataRecord.user.identifier!""}
 
Data recordClosed A Data Record is a dispatched or submitted form, or a form saved as a draft on the user's device. Each data record has an associated reference number - a dataRecordID on the server and a clientDataRecordID on the device. ID:  ${dataRecord.identifier!""}
 
First: ${answers.firstName[0]!""}
Middle: ${answers.middleName[0]!""}
Last: ${answers.lastName[0]!""}

Date Time of Incident - provided time:  ${answers.dateTimeIncident[0].provided.time!""}
Date Time of Incident - provided zone:  ${answers.dateTimeIncident[0].provided.zone!""}
 
Location of Incident - ${answers.locationOfIncident[0].address!""}
Location of Incident - latitude - ${answers.locationOfIncident[0].coordinates.latitude!""}
Location of Incident - longitude - ${answers.locationOfIncident[0].coordinates.longitude!""}

The output file looks like this:

Username: holmes
ID: 123456789

Data record ID:  18000000000

First: John
Middle: Alex
Last: Smith

Date Time of Incident - provided time:  2024-04-24T17:18:00-04:00
Date Time of Incident - provided zone:  America/Toronto

Location of Incident - 1234 Mystreet, Ottawa, ON A2B 3C4, Canada
Location of Incident - latitude 45.3474
Location of Incident - longitude -75.9189