Expressions



Intro

Besides “static” field properties like “visible”, “required” etc, it’s possible to also define “dynamic” field properties. These are called expressions and the JSON keys are suffixed with “Expression”, e.g. “visibleExpression”, “requiredExpression” etc.

The value of expression field properties is evaluated at runtime against actual values in the app, which is what makes them “dynamic”, e.g. it’s possible to define something like “Remarks field is visible only if Subject is greater than 20 characters” and the runtime app behavior will be that as soon as the user types in more than 20 characters in the Subject field the Remarks field appears automagically.


Expression types

The following JSON fields supporting expressions exist:

Expression Type Validation
“visibleExpression” Must evaluate to a boolean value.
“editableExpression” Must evaluate to a boolean value.
“requiredExpression” Must evaluate to a boolean value. See Data Validation for details how to validate required fields on the screens.
“defaultValueExpression” Must evaluate to a string, number, boolean, datetime or duration (in milliseconds) value.
“validationExpression” Must return a boolean value indicating if the entered value is valid or not.
  • There is no static “validation” property, just “validationExpression”
  • An additional property called “validationErrorMessage” is supported, containing a message to be displayed when the validation defined by the “validationExpression” fails.
  • “validationErrorMessage” - it defines the text to be displayed to the user when the field is not valid according to the above condition (if this is translated, then it will contain the from the translations file). See Data Validation for details how and when to display validation messages.

Expression Priority

If an expression is present, then the corresponding static property is ignored (for example, if “visibleExpression” is present, “visible” is ignored). However, if the expression evaluation yields an error (i.e. the expression is incorrect) then the code will fallback to the corresponding static property and use that (e.g. if “visibleExpression” cannot be evaluated for some reason, “visible” is used if present).

If both the static property and the expression are missing (for example “visible” and “visibleExpression”) the application logic is used. So, if it’s intended that the default application logic is used for a field, then in the (default) screen configuration for that field there should be nothing set for ‘visible’, ‘editable’, ‘required’, etc. and their “Expression” counterparts.

An exception to this is the “validationExpression”: the validation defined by it runs in addition to the default validation which already exists in the apps. The reason is that the default validation which already exists probably has a very good reason for existing, so by defining a “validationExpression” we should not override the existing validation.


Expression Format

All “xyzExpression” types are written in the same way: they’re some chunk of Javascript code which gets evaluated at runtime.

This code can be dependent on the following:

Application-independent

  • "new Date() > new Date(2000, 1)" meaning today’s date is later than January 2000
  • this expression code uses no data from the application itself
  • any and all built-in javascript functions and data types may be used

Screen-dependent

  • "${serviceCall.subject} === 'SuperCall'" meaning the Subject of the Service Call must match exactly the text 'SuperCall'
  • In this case "${serviceCall.subject}" is a variable which the application will substitute at runtime with its actual value
  • Please note that this value can also be not set (i.e. the user didn’t enter anything in the subject field), see below on how this will be handled at runtime and how you can write expressions to support it
  • The variable may only reference data already loaded inthe UI of the screen being customized, e.g. for the ServiceCall details screen, since it loads and shows the BusinessPartner the variable "${serviceCall.businessPartner.name}" is valid, but "${serviceCall.businessPartner.defaultContact.name}" is not

Application-dependent

  • Variables that don’t reference something from the screen, but some global object from the application
  • Similar to the screen dependent variables, they are also replaced at runtime
  • The list of the global variables that are currently supported is:
    • urrentUser: it refers to the Person DTO that refers to the usuer that is currently login, so it supports all the properties from the DTO definition
      • example: "${currentUser.firstName} === 'Joe'"
    • currentProfile: it refers to the ProfileObject DTO (there should be only one for each company), so it supports all the properties from the DTO definition: ProfileObject DTO
      • example: "${currentProfile.userAccountGroupName} === 'ALL'"

Customer-specific

  • "satisfiesCustomRule(${serviceCall.subject})"
  • In this case “satisfiesCustomRule” is a customer defined function which can be added in the cloud by the customer herself
  • At runtime the application will substitute the value of the “${serviceCall.subject}” variable and the function will be called
  • The code of the function is completely the responsibility of the customer, the application doesn’t validate it in any way
  • Only simple functions are supported, i.e. no promises, http calls etc
  • The cloud DTO used for this is “Function DTO” and they can be managed in the admin portal

A Mix of Dependencies

  • "isValidSubject(${serviceCall.subject}) && new Date() > new Date(2000, 1) && ${serviceCall.remarks}.length > 20"

Variable Substitution

Since the expressions can contain variables, the actual values for this variables must be substituted by the apps at runtime before evaluating the expressions.

The following (conceptual) variable data types are supported:

  • string
  • number
  • datetime
  • boolean
  • duration

Since each application represents these data types differently (technology-specific), each application is responsible for converting from its representation to a valid Javascript representation of the value before evaluating an expression.

However, independent of application/technology there are some common guidelines for this conversion so as to ensure that the Javascript expression evaluation works the same independent of platform.

Here’s how each data type must be converted to its equivalent Javascript value:

Data Type Conceptual value Javascript Value Example(s) Remarks
string something ‘something’ Expression: "${person.name}.length > 20"

Value: Adrian

After substitution: "'Adrian'.length > 20"
So in the expression there are no quotes, the app will take the actual value and wrap it in quotes.
number 3.14 3.14 Expression: "${mileage.distance} > 20"

Value: 55.3

After substitution: "55.3 > 20"
The conversion is done using US/UK standards, i.e. if the app is displayed in german and the value is “3,14” after conversion in Javascript it will be “3.14”
datetime value new Date(value_as_unixtimestampmilliseconds) Expression: "${serviceCall.startDate} > new Date()"

Value: whatever serviceCall.startDate will be

After substitution: "new Date(xxx) > new Date()" where xxx is the value of serviceCall.startDate converted to a unix timestamp in milliseconds
There are several potential options for converting datetimes:simply unix timestampstringjavascript DateThe latter gives the most flexibility, as there are also methods available to call on it, for example if you want to check that the date is in December you can do "${serviceCall.startDate}.getMonth() == 11" which at runtime will be converted into "new Date(value).getMonth() == 11"
duration value   Expression: "${effort.duration} > 1000"

Value: value_in_milliseconds

After substitution: "value_in_milliseconds > 1000"
Plesae note that this example uses US/UK time notation standard.
boolean value True / False Expression: "${activity.isPersonal}"

Value: true

After substitution: true
 
array array with objects: "string1", "string2" ['string1', 'string2'] Expression: "${someArrayOfStrings}.length > 0"

Value: ['string1', 'string2']

After substitution: "['string1', 'string2'].length > 0"::nomarkdown}

{:/} Expression: "${relation.checklists}.length > 0"

Value: [checklist1DTO, checklist2DTO]

After substitution: "['checklist1ID', 'checklist1ID'].length > 0"
There are two types of array supported:arrays of basic types (the ones above) - see example 1arrays of DTO types, in this case the elements of the array will be, by convention, the cloud ids of those DTOs - see example 2

If it does not have a value (e.g the user didn’t enter any data yet):

  • For all data types the Javascript representation should simply be “undefined” (without the quotes)
  • This way for example if we want to have the Remarks field visible only if ServiceCall.Subject has a value the visibleExpression would be "${serviceCall.subject} !== undefined" and if the Subject has a value "abc" it would at runtime become " 'abc' !== undefined" which evaluates to true.
  • When validating a value we must always consider the case that the value might be empty.
    • Example: Check if the field value has a certain length: "validationExpression": "${mileage.source} != undefined && ${mileage.source}.length > 3"