Accessing Payload Data

There are a few different methods for accessing and manipulating data from workflow payloads and device data queries. Depending on the use case, you may run across a point while building your app where you’re asked to supply a data reference in one of the following formats.

{
  "data" : {
    "aNumberProp": 42,
    "aStringProp": "Hello world",
    "aBooleanProp": true,
    "aFalsyProp" : 0,
    "aDecimal": 123.456789,
    "anUndefinedProp": undefined,
    "aNullProp": null,
    "aJavascriptDateObject": Wed Sep 28 2016 15:40:25 GMT-0400 (EDT),
    "anArray": [
      44,
      "Goodbye world",
      false,
      {
        "anObjectPropInAnArray": "I'm deep!"
      }
    ],
    "anObject": {
      "aNestedArray": [
        "I'm a string!",
        20,
        { }
      ],
      "aNestedNumber": 56,
      "aNestedString": "Wally World"
    },
    "anHtmlString": "<span>Wall-E</span>"
  }
}

Payload Paths

Many workflow nodes, such as the HTTP Node, the Get and Store Value nodes and the Math Node, require users to define a payload path at which either data will be accessed for evaluation, or at which the returned result of an operation will be stored. In these cases we use payload paths.

Payload paths are dot-separated references to object properties, such as foo.bar. If any property in the chain returns undefined, the value of the payload path will also return undefined.

Square brackets wrapped around a property name can be used in a handful of cases:

  • Accessing an array value by index. e.g. data.anArray.[0] returns the first value in the array at that path.
  • Accessing a specific character of a string by index. e.g. data.aString.[1] returns the second letter of the string at that path.
  • Escaping payload properties that have spaces or other special characters. e.g. data.[a silly property name].

Examples

Given our example object, here is how we would reference many of the properties using payload paths:

data.aNumberProp // 42
data.aBooleanProp // false
data.aNullProp // null
data.aJavascriptDateObject // Wed Sep 28 2016 15:40:25 GMT-0400 (EDT)

. // (returns the entire example object)

data.anArray.[1] // "Goodbye world"
data.anArray.1 // (invalid syntax)
data.anArray.[1].[2] // o (The character at index 2 of the array item at index 1)
data.anArray.[3] // { "anObjectPropInAnArray": "I'm deep!" }
data.anArray.[3].anObjectPropInAnArray // "I'm deep!"
data.anArray.[8] // (undefined. This index does not exist on the array.)

data.anObject.aNestedNumber // 56
data.anObject.aNestedArray // ["World Star!", 20, { }]
data.anObject.aNestedArray.[1] // 20

data.aNonexistentObject.aMissingProp // (undefined. "aNonExistentObject" is not a property of "data")
aNumberProp // (undefined. This is not a top-level property of our object.)

Payload paths do not accept variables within the path definition; the paths must be static references.

String Templates

String templates are Handlebars templates – that is, they are constructed using a payload path that is wrapped in double curly brackets, such as {{foo.bar}}. The value at the specified path should resolve to a printable property; specifically, the value should be of the type “string”, “number”, “boolean” or “undefined”. Objects referenced in this way will return [object Object], and arrays will return as comma-concatenated strings of all array values, such as foo,bar,42,false.

Areas within the app that call for a string template do not necessarily have to include a variable; it is perfectly acceptable to put a static string value in place of a template.

String templates appear in a number of places within the Losant Platform; in all cases, they serve one of two purposes:

Embedded Workflows

Note: The HTML-escaping, block helpers, and format helpers described below are not valid within Embedded Workflows. In node configuration properties that accept a string template, Embedded Workflows will only accept a direct reference to the value.

Escaping HTML

By default, HTML within a Handlebars helper is escaped to prevent malformed HTML from breaking the page layout. If you wish to override this feature, put your string templates inside of triple curly brackets, e.g. {{{aValueWithHtmlTags}}}. You may, for example, wish to include HTML tags in the message body of the Email Node.

Conditional Block Helpers

Conditional block helpers allow for rendering content if a given condition returns true; they also support rendering content in case the condition is not met, using the {{else}} syntax. For example:

{{#if experience.user}}Hello, {{experience.user.firstName}}!{{else}}User is not signed in.{{/if}}

Values in block helpers can themselves be wrapped in subexpressions, such as:

{{#eq (lower experience.user.userTags._role) 'administrator'}}Hello, Admin!{{else}}Hello, User!{{/eq}}

The following block helpers are available. If you have an idea for a new helper, please let us know about it in our forums.

{{#if val}}

Conditionally renders its inner content if val is not false, undefined, null, "" (empty string), 0, or [] (empty array). This is a built-in Handlebars helper and is not specific to Losant.

{{#unless val}}

The inverse of {{#if}}; it renders its inner content if val is false, undefined, null, "" (empty string), 0, or [] (empty array). This is a built-in Handlebars helper and is not specific to Losant.

{{#eq p1 p2}}

Renders its inner content if p1 and p2 are equal by shallow comparison.

{{#ne p1 p2}}

Renders its inner content if p1 and p2 are not equal.

{{#gt p1 p2}}

Renders its inner content if p1 is greater than p2.

{{#gte p1 p2}}

Renders its inner content if p1 is greater than or equal to p2.

{{#lt p1 p2}}

Renders its inner content if p1 is less than p2.

{{#lte p1 p2}}

Renders its inner content if p1 is less than or equal to p2.

{{#match str regExpStr}}

Renders its inner content if str matches the regular expression regExpStr.

{{#includes collection value}}

Renders its inner content based on the following:

  • If collection is a array and value is equal (by deep comparison) to an entry in that array.
  • If collection is an object and value is equal (by deep comparison) to one of the top-level property values.
  • If collection is a string and value (when cast to a string) is equal to or matches part of that string.

Iterating Over Values

One very useful feature of string templates is the ability to iterate over arrays, objects, strings, and numbers using the {{#each}} block helper (another native Handlebars helper). When doing so, a few variables become available to the user in the context of the loop:

  • {{@first}} will be true if this is the first iteration of the loop, and will be false otherwise.
  • {{@index}} returns a number corresponding to the index of the current item in the loop. One possible use of this value is to know how many times the loop has executed, and to stop executing additional code within the loop based on this value.
  • {{@key}} returns the name of the current key when iterating over an object, otherwise it will be the same as {{@index}}. You can use this to execute a different string template based on the current item in the loop.
  • {{@last}} will be true if this is the last iteration of the loop, and will be false otherwise.
  • {{this}} represents the current item in the loop; or, put another way, the value at {{anArray.[@index]}} or {{anObject.[@key]}}.

When iterating over a string, the string is treated as an array of the individual characters of the string. When iterating over a number, the number is treated as an array of the integers starting at 0 all the way up to (but not including) the given number.

The {{else}} statement, when used within an {{#each}} block, will render its contents if the value provided to the {{#each}} helper is empty. This allows for easily creating list empty states in Experience Views.

Note: When inside an {{#each}} block helper, the render context changes to the current iterated value. If you need to access a property that is outside of this context, you may do so one of two ways:

  • Use ../ in front of your variable name to step up one level in the context object
  • Use @root as the first segment in the variable to start at the root of the context object

For example, given a payload of:

{
  "city": "Cincinnati",
  "state": "Ohio",
  "streets": ["Broadway", "Sycamore", "Main"]
}

And a string template of:

<ul>
  {{#each streets}}
    <li>{{this}}, {{../city}}, {{@root.state}}</li>
  {{/each}}
</ul>

The output would be the following:

<ul>
  <li>Broadway, Cincinnati, Ohio</li>
  <li>Sycamore, Cincinnati, Ohio</li>
  <li>Main, Cincinnati, Ohio</li>
</ul>

Changing Evaluation Context

By wrapping your content in the native Handlebars {{#with}} block helper, you can change the evaluation context of any helper within the block. This can make it easier to reference deeply nested properties and avoid collisions with other variable names.

The {{else}} statement, when used within a {{#with}} block, will render its contents if the value provided to the {{#with}} helper is empty.

Format Helpers

Handlebars helpers can also mutate a given value in place and print the result. Losant provides the following formatting helpers:

{{add val1 val2}}

Casts the two values as numbers and adds them.

{{array value1 value2 value3}}

Creates an array out of all of the given arguments. Attempting to print this value directly will output the items joined by a comma; this helper is used almost exclusively to construct arguments for other helpers inline. For example:

{{jsonEncode (array pair.key pair.value)}}

{{ceil val}}

Casts the value as a number and returns the ceiling of that number. See Math.ceil().

{{colorMarker color}}

Renders a dashboard pin using the provided color argument. This helper is most useful when conditionally rendering points in a GPS History Block.

{{currentDateTime formatStr tz="UTC" locale="en"}}

Returns the current date/time formatted by the provided format string. The format string follows Moment.js format syntax, and the default is ‘L LTS’.

The tz and locale arguments are optional. See {{formatDate}} for more information.

{{divide val1 val2}}

Casts the two values as numbers and divides them.

{{dashboardUrl id queryParamKey1='queryParamValue1' ...}}

Creates a link to the dashboard at the given ID (defaults to the current dashboard) and adds the optional query parameters. To pass a nested object of parameters, such as for dashboard context, include the parameter as ctx=(obj key1=valueFromField key2="staticValue").

{{decodeBase64 val}}

Treats val as Base64-encoded and returns a decoded string (UTF-8 character set).

{{decodeURI str}}

Returns str as a decoded URI.

{{decodeURIComponent str}}

Returns str as a decoded URI component.

{{defaultTo val default}}

Returns the value of default if the value of val is null, undefined, or an empty string. Otherwise returns the value of val.

{{encodeBase64 val}}

Treats val as a string, and returns the Base64-encoded version of that string (UTF-8 character set).

{{encodeURI str}}

Returns str as an encoded URI.

{{encodeURIComponent str}}

Returns str as an encoded URI component.

{{evalExpression str ctx}}

Evaluates str as an expression, using ctx as the context for variable lookups. If ctx is not provided, the overall payload is used. Cannot be used recursively, or combined with template.

{{floor val}}

Casts the value as a number and returns the floor of that number. See Math.floor().

{{format val formatStr tz="UTC" locale="en"}}

  • If val is a number, returns the number in the D3 format matching the formatStr parameter (default “,.6”).
  • If val is a JavaScript Date object, returns the date in the Moment.js format matching the formatStr parameter (default “L LTS”).
  • If val is an object, returns the stringified object and ignores other parameters. (See {{jsonEncode}} for better rendering of stringified objects.)
  • For all other formats, val is returned as a string without mutation.

The tz argument is optional and only applies if val is a Date object. See {{formatDate}} for more information.

The locale argument is optional. It accepts a locale code and defaults to the user’s current browser’s locale in - or, in server settings (such as in workflow executions), it defaults to “en” (English).

  • When rendering a number with a locale argument, this renders the number as is done in the provided locale (separators and decimals).
  • See {{formatDate}}) for the behavior when rendering a date.

When locale is not provided, it defaults to the browser’s locale in client-side usages (such as when rendering values in a dashboard description or the workflow template tester). In server settings (such as in workflow executions and experience page rendering), it defaults to “en” (English).

{{formatDate val formatStr tz="UTC" locale="en"}}

Casts the given value as a date, and then formats it using the formatStr parameter (defaults to “L LTS”).

The tz argument is optional; it accepts a timezone name for rendering the date against the timezone’s offset from UTC.

The locale argument is optional; it accepts a locale code to use for rendering the date in the given locale’s standard format (though certain formatStr arguments may override this argument’s effect).

When this helper is used in a browser setting (such as when rendering values in a dashboard description or the workflow template tester), tz defaults to the user’s current timezone and locale defaults to the browser’s locale setting. In server settings (such as in workflow executions and experience page rendering), tz defaults to “UTC” and locale defaults to “en” (English).

{{formatDateRelative date relativeTo locale="en"}}

Outputs a relative date value (such as “5 minutes ago”). Date is calculated relative to the relativeTo parameter, which defaults to the current time when not set.

The locale argument is optional. See {{formatDate}} for more information.

{{formatGps gpsStr formatStr precision}}

Takes the given GPS coordinate, in any of the supported formats, and reformats to the desired format. formatStr can be decimal (the default), degrees, sexagesimal, or gga. precision will round the coordinates to the given number of decimal places (default 6).

{{gpsDistance gpsStr1 gpsStr2}}

Takes two GPS points and returns the distance between them (in meters).

{{gpsIsPointInside point polyArray}}

Takes a GPS point and an array of points making up a polygon geofence, and renders “true” if point is within the bounds of polyArray and “false” if not.

For example, given the following context …

{
  "point": "40,60",
  "polyArray": [
    "0,0",
    "45,45",
    "45,90",
    "0,45",
    "0,0"
  ]
}

… and the following use of the {{gpsIsPointInside}} helper …

{{gpsIsPointInside point polyArray}}

… the output would be:

true

{{indexByKey objArray keyValue keyPath}}

Returns the index of the first object in the objArray whose value at keyPath deeply matches keyValue. The argument keyPath is optional, defaulting to the path key. If there is no match found, -1 is returned.

For example, given the following context …

{
  "objArray": [
    { "age": 86, "name":  { "first": "Alan", "last": "Alda" } },
    { "age": 98, "name":  { "first": "Bob", "last": "Barker" } },
    { "age": 46, "name":  { "first": "Candace", "last": "Cameron" } }
  ]
}

… and the following use of the {{indexByKey}} helper …

{{indexByKey objArray 'Bob' 'name.first'}}

… the output would be:

1

{{join array separator}}

Joins the values in the given array as a single string, separated by the given separator. If no separator is given, defaults to ,.

{{jsonDecode val}}

Treats val as a JSON string and parses it. If not parsable, will return undefined. This helper is used almost exclusively to construct arguments for other helpers inline.

{{jsonEncode val spacerStr}}

Returns val stringified. If no spacer string is given (for adding whitespace for readability to the output), defaults to adding no whitespace.

{{last val}}

Returns the last item in an array or the last character in a string. If there are no items in the given array or string, or if the input value is not one of those types, the helper returns undefined and renders an empty string.

{{length val}}

Returns the following string based on the type of val:

  • If val is an array, returns the number of items in the array. See Array.length.
  • If val is an object, returns the number of keys on the object. See Object.keys().
  • If val is a string, returns the length of the string. See String.length.
  • For everything else, returns undefined.

{{lookup collection property}}

Allows for dynamic parameter resolution using Handlebars variables. This is a built-in Handlebars helper and is not specific to Losant.

{{lower str}}

Returns str converted to all lowercase characters.

{{max val1 val2}}

Returns the larger of the two arguments. See Math.max().

{{merge val1 val2 val3}}

Shallow merges all the given arguments into a single object. If keys are duplicated, the value from the last argument with that key is used. Attempting to print this value directly will output [object Object]; this helper is used almost exclusively to construct arguments for other helpers inline.

{{min val1 val2}}

Returns the smaller of the two arguments. See Math.min().

{{multiply val1 val2}}

Casts the two values as numbers and multiplies them.

{{obj key1=val1 key2=val2 ...}}

Constructs a hash of the provided key-value pairs. Attempting to print this value directly will output [object Object]; this helper is used almost exclusively to construct arguments for other helpers inline. For example:

{{dashboardUrl 'a1b2c3d4e5f6a1b2c3d4e5f6' ctx=(obj deviceId=request.params.deviceId expUser=experience.user.id)}}

{{queryStringEncode object}}

Takes the given object and converts it to a URL query string. Note: In almost all cases, this helper should be HTML-escaped by wrapping the helper in three curly braces, like so:

{{{queryStringEncode myQueryObject}}}

{{scaleLinear fromLow fromHigh toLow toHigh value}}

Scales the given value from the domain defined by fromLow and fromHigh to the range defined by toLow and toHigh. Similar to the Arduino Map function.

For example, given the following use of the {{scaleLinear}} helper …

{{scaleLinear 0 100 0 10000 10}}

… the output would be:

1000

{{substring sourceStr startIdx optionalEndIdx}}

Returns sourceStr from the start index up to and excluding the end index, or to the end of the sourceStr if no end index is supplied. See String.prototype.substring().

{{subtract val1 val2}}

Casts the two values as numbers and subtracts them.

{{template str ctx}}

Evaluates str as string template, using ctx as the context for variable lookups. If ctx is not provided, the overall payload is used. Cannot be used recursively, or combined with evalExpression.

{{toHtml object}}

Takes the given object and converts it to an HTML/XML document string. The object must be of the same format that the HTML Parser Node outputs in JSON mode.

{{trim str}}

Returns str with all whitespace trimmed from the start and end.

{{titleCase str}}

Returns str with title capitalization rules applied.

{{typeof val}}

Returns the following string based on the type of val:

  • If val is a number, returns number.
  • If val is a boolean, returns boolean.
  • If val is a string, returns string.
  • If val is null, returns null.
  • If val is undefined, returns undefined.
  • If val is an array, returns array.
  • If val is a date object, returns date.
  • If val is an object, returns object.

{{upper str}}

Returns str converted to all uppercase characters.

{{valueByKey objArray keyValue keyPath valuePath}}

Returns the value at valuePath on the first object in the objArray whose value at keyPath deeply matches keyValue. The arguments keyPath and valuePath are optional, defaulting to the paths key and value. If there is no match found, a blank result is returned.

For example, given the following context …

{
  "objArray": [
    { "age": 86, "name":  { "first": "Alan", "last": "Alda" } },
    { "age": 98, "name":  { "first": "Bob", "last": "Barker" } },
    { "age": 46, "name":  { "first": "Candace", "last": "Cameron" } }
  ]
}

… and the following use of the {{valueByKey}} helper …

{{valueByKey objArray 'Bob' 'name.first' 'name.last'}}

… the output would be:

Barker

String Template Examples

Using our example object from above, let’s see what each of these string templates would print …

{{data.aNumberProp}} => 42
{{data.aStringProp}} => Hello world
{{data.aBooleanProp}} => true
{{data.anUndefinedProp}} => (prints nothing)
{{data.aMissingProp}} => (prints nothing)
{{data.anArray.[1]}} => Goodbye world
{{data}} => [object Object]

{{data.anHtmlString}} => &lt;span&gt;My New Post&lt;/span&gt;
{{{data.anHtmlString}}} => <span>Wall-E</span>

{{data.anArray}} => 44,Goodbye world,false,[object Object]
(all items in the array concatenated by commas)

{{data.anArray.[1].[0]}} => G
(the character at index 0 of the item at index 1 of the array)

{{data.anArray.0}} => (invalid syntax)
(0 is not a property of the array; should be [0] to access by index)

{{data.anArray.[16]}} => (prints nothing)
(index does not exist on the array.)

{{#if data.aNumberProp}}The number is {{data.aNumberProp}}{{/if}}
=> The number is 42

{{#if data.aMissingProp}}The number is {{data.aMissingProp}}{{/if}}
=> (prints nothing. The condition fails.)

{{#if data.aFalsyProp}}Truth!{{else}}Fiction!{{/if}}
=> Fiction! (0 is falsy so the condition falls to the else statement.)

{{#each data.anArray}}{{@index}}: {{this}}; {{/each}}
=> 0: 44; 1: Goodbye world; 2: false; 3: [object Object];

{{#each data.anObject}}{{@key}}: {{this}}; {{/each}}
=> aNestedArray: World Star!,20,[object Object]; aNestedNumber: 56; aNestedString: Wally World;

{{#each data.aMissingObject}}{{@key}}: {{this}} -- {{else}}No object!{{/each}}
=> No object!

{{#gt data.aNumberProp 40}}First number is greater{{/gt}}
=> First number is greater

{{#lte data.aNumberProp 10}}Second number is greater{{/lte}}
=> (prints nothing)

{{#eq data.aStringProp 'hello world'}}Equal!{{else}}Not equal{{/eq}}
=> Not equal (cases don't match)

{{#eq (lower data.aStringProp) 'hello world'}}Equal!{{else}}Not equal{{/eq}}
=> Equal! (cases match after running through the "lower" subexpression)

{{format data}} => (the stringified object)
{{format data.aDecimal ',.5'}} => 1,234.6
{{encodeURIComponent data.aStringProp}} => Hello%20world

{{#eq (format data.aJavascriptDateObject 'MMMM') 'September'}}Ba de ya!{{else}}Play something else{{/eq}}
=> Ba de ya!

Expressions

Sometimes it is necessary to evaluate our data and determine which of two (or many) paths to follow as a result of the evaluation, such as in the dashboard Indicator block’s expressions and the workflow Conditional Node. In these cases we use expressions.

An expression is a combination of string templates and JavaScript operators that forms a “JavaScript-like” expression after parsing through the Handlebars engine. The expression returns a value; depending on the use case, that value may be evaluated as truthy or falsy to determine which branch a workflow follows (as in the Conditional Node), or the value may be added to a payload (as in the Math Node).

Any operators or functions applied to a string template within an expression should be placed outside of the string template; for example, {{foo}} > {{bar}} or !{{bat}}.

String templates used within expressions may not use block helpers; they may, however, use format helpers.

Note that, when doing string comparisons in expressions, it is not necessary to wrap any string template in quotes to “stringify” the output. The expression will be evaluated with the output of the string template serving as a variable; for example {{foo}} === 'bar' or {{bat}} !== {{baz}}.

Below are lists of operators, keywords and functions supported in expressions. If you have use cases that are not handled by these, please let us know in our forums.

Supported Operators

Expressions support the following operators:

Note: In Embedded Workflows, equality operators are always treated as strict (as in, == behaves as === and != behaves as !==).

Supported Keywords

Expressions have support for strings and numbers, as well as the following special keywords:

  • true (boolean true)
  • false (boolean false)
  • null (JSON null)
  • undefined (JSON undefined)
  • E (e, approximately 2.718)
  • PI (π, approximately 3.14159)

Supported Functions

Expressions also support the following functions:

sin(radians)

Returns the sine of an angle provided in radians. See Math.sin().

cos(radians)

Returns the cosine of an angle provided in radians. See Math.cos().

tan(radians)

Returns the tangent of an angle provided in radians. See Math.tan().

asin(sinVal)

Returns the arcsine of a provided sine value. The result is in radians. See Math.asin().

acos(cosVal)

Returns the arccosine of a provided cosine value. The result is in radians. See Math.acos().

atan(tanVal)

Returns the arctangent of a provided tangent value. The result is in radians. See Math.atan().

sinh(val)

Returns the hyperbolic sine of a number. See Math.sinh().

cosh(val)

Returns the hyperbolic cosine of a number. See Math.cosh().

tanh(val)

Returns the hyperbolic tangent of a number. See Math.tanh().

asinh(val)

Returns the hyperbolic arcsine of a number. See Math.asinh().

acosh(val)

Returns the hyperbolic arccosine of a number. See Math.acosh().

atanh(val)

Returns the hyperbolic arctangent of a number. See Math.atanh().

atan2(y, x)

Returns the arctangent of the two variables y and x, which together represent the coordinate in the plane. The result is in radians. See Math.atan2().

sqrt(val)

Returns the square root of a number. See Math.sqrt().

log(val)

Returns the natural logarithm (base e) of a number. See Math.log().

log10(val)

Returns the base-10 logarithm of a number.

abs(val)

Returns the absolute value of a number. See Math.abs().

ceil(val)

Returns the smallest integer greater than or equal to a number. See Math.ceil().

floor(val)

Returns the largest integer less than or equal to a number. See Math.floor().

round(val)

Rounds a number to the nearest integer. See Math.round().

trunc(val)

Truncates any fractional digits of a number, returning the integer portion. See Math.trunc().

exp(exponent)

Computes e raised to the power of the given exponent. See Math.exp().

pow(base, exponent)

Raises a number base to the power of exponent. See Math.pow().

includes(collection, value)

Returns true in the following cases:

  • If collection is a array and value is equal (by deep comparison) to an entry in that array.
  • If collection is an object and value is equal (by deep comparison) to one of the top-level property values.
  • If collection is a string and value (when cast to a string) is equal to or matches part of that string.

Otherwise returns false.

Note: This function is not supported in Embedded Workflows.

length(val)

Computes the following string based on the type of val:

  • If val is an array, returns the number of items in the array. See Array.length.
  • If val is an object, returns the number of keys on the object. See Object.keys().
  • If val is a string, returns the length of the string. See String.length.
  • For everything else, returns null.

Note: This function is not supported in Embedded Workflows.

max(a, b)

Returns the maximum of the two arguments. See Math.max().

min(a, b)

Returns the minimum of the two arguments. See Math.min().

Expression Examples

With the provided functions, keywords and operators, it is possible to build very complex expressions, such as …

sin({{ data.distance }}) >= 0.25 * PI && ({{ data.open }} !== undefined || !{{ data.closed }})

Given our example object, let’s see how these simpler expressions evaluate.

{{data.aBooleanProp}} // true
{{data.aBooleanProp}} === false // false
{{data.aNumberProp}} === 'hello' // false
{{data.aFalsyProp}} == false // true
{{data.aFalsyProp}} === false // false
!!{{data.anUndefinedProp}} // false

{{data.aNumberProp}} > 20 // true
{{data.anObject.aNestedNumber}} - {{data.aNumberProp}} // 14

{{data}} && {{data.aBooleanProp}} // true
{{data.aBooleanProp}} || {{data.aFalsyProp}} // true
{{data.aBooleanProp}} && {{data.aFalsyProp}} // false

{{data.aNumberProp}} > {{data.anObject.aNestedArray.[1]}} // true
{{data.aNumberProp}} > {{data.aStringProp}} // false
{{data.aNumberProp}} < {{data.aStringProp}} // false


{{format data.aJavascriptDateObject 'MMMM'}} === 'September' // true
{{lower data.aStringProp}} === 'Hello world' // false

{{data.anObject.aNestedArray.[1]}} / 5 <= PI // false
pow({{data.aNumberProp}}, {{data.aFalsyProp}}) === 1 // true

JSON Templates

In a few places within the Losant Platform – such as the Input Controls Block and Losant API Node, it is necessary to construct JSON objects from other data. In these instances we use JSON templates.

JSON templates can take any of the following formats (or a combination thereof):

  • Valid, static JSON. e.g. {"foo": "bar"}, [1,2,3] or null
  • JSON containing block helpers around keys, values or both. e.g. { {{#if foo}}"bar": "bat"{{else}}"baz": "bub"{{/if}} }
  • JSON containing string helpers for keys or values. e.g. {"month": "{{format date 'MMMM'}}" }
  • Standard handlebars payload references, e.g. {{data.nestedObject.nestedArray.[1]}}

Your entire input will run through Handlebars and the Losant-provided helpers. After evaluation, the result must be valid JSON. Because of this, there are a few things to keep in mind when using string templates within your JSON templates:

  • Any string value being output by a string template should be wrapped in double quotes, like so: { "foo": "{{data.aStringProp}}" }.
  • Any boolean or number value being output by a string template should not be wrapped in quotes, unless you want the value passed as a string: { "foo": {{data.aNumberProp}} }.
  • Any objects or arrays being referenced by a string helper should always use the jsonEncode Handlebars helper to ensure that all string values are wrapped in quotes and all objects print as valid JSON, and should be wrapped in triple curly braces to prevent escaping of characters: { "anObject": {{{jsonEncode data.anObjectProp}}} } or { "anArray": {{{jsonEncode data.anArrayProp}}} }.

JSON Template Examples

Again, given our example object above, these JSON templates will evaluate as follows …

{ "foo": "bar", "staticNumber" : 66 }
// { "foo": "bar", "staticNumber" : 66 }

{ "myArray": {{{jsonEncode data.anArray}}} }
// { "myArray": [44, "Goodbye world", false, {"anObjectPropInAnArray": "I'm deep!"} }

{ "timestamp": {{format data.aJavascriptDateObject 'x'}}, "dayOfWeek": "{{format data.aJavascriptDateObject 'dddd'}}", "capitalString": "{{upper data.aStringProp}}" }
// { "timestamp": 1475091625000, "dayOfWeek": "Wednesday", "capitalString": "HELLO WORLD"}

{ "myString": {{data.aStringProp}} }
// FAILS since the string is not wrapped in double quotes

{{{jsonEncode data.anArray}}}
// [44, "Goodbye world", false, {"anObjectPropInAnArray": "I'm deep!"}

{{data.nestedObject.nestedArray.[1]}}
// 20

{{data.nestedObject.nestedArray.[0]}}
// FAILS because the referenced value is a string, which is not valid JSON

{ {{#gt data.aNumberProp 21}}"highNumber"{{else}}"lowNumber"{{/gt}}: {{data.aNumberProp}} }
// { "highNumber": 42 }

Was this page helpful?


Still looking for help? You can also search the Losant Forums or submit your question there.