Skip to main content

Logic Expressions

Overview

Logic expressions are powerful evaluative statements used to create dynamic, highly interactive, and personalized forms. By evaluating conditions - such as a respondent's previous answers, user metadata, or calculated variables - expressions allow your survey to automatically adapt its behavior and UI in real time. They act as the decision-making engine behind your form, enabling you to conditionally control element visibility, enforce complex validation rules, calculate values dynamically, and trigger automated form actions, without the need to do any custom programming per form.

Expressions can be used in the following areas of your Endatix forms:

  • Conditional Visibility: Dynamically show or hide questions, panels, or entire pages based on specific criteria (e.g., showing a follow-up question only if the user selects "Other").
  • Dynamic Read-Only State: Enable or disable form elements conditionally, preventing respondents from interacting with certain fields unless prerequisite conditions are met.
  • Conditional Requirements: Make a question mandatory only when a specific scenario occurs, rather than applying a blanket requirement.
  • Real-Time Calculations: Used heavily within the Expression question type or the form's Calculated Values to compute mathematical equations, string concatenations, or logical operations dynamically.
  • Advanced Data Validation: Used to enforce strict, custom validation rules that rely on the state of multiple fields (e.g., ensuring the sum of three percentage fields equals exactly 100).
  • Choice Filtering: Dynamically hide, show, or disable specific individual options within choice-based questions (like Dropdowns, Checkboxes, or Radiogroups) based on previous selections.
  • Automated Form Triggers: Expressions serve as the execution condition for automated background actions. When the expression evaluates to true, the form can automatically:
    • Set Values: Assign a specific value to a target question.
    • Copy Values: Copy an answer from one field to another (e.g., "Same as Billing Address").
    • Skip Logic: Jump the respondent directly to a specific target question or page.
    • Complete Survey: Instantly submit and finish the survey based on a qualifying or disqualifying response.
    • Run Custom Logic: Execute another background expression or custom function.

Logical & State Functions

The following functions and operators help you evaluate conditions and check the current state of your form, its containers, or any of its questions. They can be used enforce the conditional visibility of individual questions, panels, or pages, and to control the flow of the form or survey.

anyAnswered

The anyAnswered function checks a specific, explicit list of questions and returns true if at least one of them has a value (is not empty).

Syntax

anyAnswered({question_1}, {question_2}, {question_3})

JSON Example

If you want to show a comment box only if the user has answered at least one of three specific questions, you can set the visibleIf property to:

{
"type": "comment",
"name": "followUpQuestion",
"title": "Please provide more details:",
"visibleIf": "anyAnswered({brands_shoes_nike}, {brands_shoes_adidas}, {brands_shirts_puma})"
}

anyAnsweredByPrefix

The anyAnsweredByPrefix function checks all questions in your form that start with a specific text prefix. It returns true if at least one matching question has been answered. This is highly recommended for large assessments or sectioned forms where questions share a common naming convention.

Syntax

anyAnsweredByPrefix('your_prefix_here')

:::info Use Single Quotes Instead of Curly Braces Unlike expressions that require specific question names, anyAnsweredByPrefix requires you to pass the prefix as a string enclosed in single quotes ''. :::

JSON Example

If you have 50 questions that all start with the prefix brands_sports_shoes_, you can check if any of them are answered using a single, short statement:

{
"type": "comment",
"name": "sectionFeedback",
"title": "Section Feedback",
"visibleIf": "anyAnsweredByPrefix('brands_sports_shoes_')"
}

Limitations with Dynamic Matrices

If you use anyAnsweredByPrefix inside a Dynamic Matrix (matrixdynamic) or Dynamic Panel (paneldynamic), it will check the questions across the entire grid globally, rather than restricting its check to the specific row the user is currently interacting with. For row-specific logic, use the standard anyAnswered({row.question_name}) syntax instead.


iif

Returns a specific value if a condition is true, and a different value if the condition is false.

  • Syntax: iif(condition, valueIfTrue, valueIfFalse)
  • Example: "iif({question1} + {question2} > 20, 'High', 'Low')"

isContainerReady

Returns true if all questions in a given panel or page have valid input; otherwise, returns false. An empty question is considered valid as long as it is not marked as required and has no other validation rules.

  • Syntax: isContainerReady(nameOfPanelOrPage)
  • Example: "isContainerReady('page1')"

isDisplayMode

Returns true if the survey is currently in display or preview mode.

  • Syntax: isDisplayMode()
  • Example: "isDisplayMode()"

Date & Time Functions

Use these functions to perform calculations based on dates, such as calculating ages or differences between two dates.

age

Returns the age based on a given birthdate.

  • Syntax: age(birthdate)
  • Example: "age({birthdate})"

currentDate

Returns the current date and time.

  • Syntax: currentDate()
  • Example: "currentDate()"

today

Returns the current date, or a date shifted by a specific number of days. For instance, today(-1) gives yesterday's date, and today(1) gives tomorrow's date.

  • Syntax: today(daysToAdd?)
  • Example: "today(2)"

year, month, day, weekday

Extracts specific parts of a given date:

  • year(date?): Returns the year.
  • month(date?): Returns the month as a number from 1 to 12.
  • day(date?): Returns the day of the month from 1 to 31.
  • weekday(date?): Returns the day of the week from 0 (Sunday) to 6 (Saturday).

getDate

Converts a question's response into a usable Date format.

  • Syntax: getDate(question)
  • Example: "getDate({birthdate})"

dateAdd

Adds or subtracts a specified amount of time (days, hours, minutes, seconds, months, or years) to a given date.

  • Syntax: dateAdd(date, numberToAdd, interval)
  • Example: "dateAdd({startDate}, 14, 'days')"

dateDiff

Returns the difference between two dates in days, hours, minutes, seconds, months, or years.

  • Syntax: dateDiff(fromDate, toDate, interval)
  • Example: "dateDiff({birthdate}, today(), 'months')"

Formatting Functions

Endatix Hub provides built-in formatting functions that you can use within your form expressions to dynamically format currency, numbers, and dates. These functions are evaluated at runtime and work seamlessly alongside standard SurveyJS expression syntax, allowing you to create personalized, localized, and professional-looking forms.

For a broader understanding of how logic and expressions work within Endatix and SurveyJS, please refer to our guide on Logic and Expressions and the official SurveyJS Conditional Logic and Dynamic Texts guide.


formatCurrency

Formats a numeric value as a localized currency string.

Syntax:

formatCurrency(value, [currencyCode], [locale])

Parameters:

  • value (required): The numeric value to format.
  • currencyCode (optional): The ISO 4217 currency code (default: "USD"). Examples: "EUR", "GBP", "JPY".
  • locale (optional): The BCP 47 language tag for formatting (default: your browser's locale). Examples: "en-US", "de-DE".

Examples:

{
formatCurrency(1234.56);
}
// Output: $1,234.56 (assuming US locale)

{
formatCurrency({ totalAmount }, "EUR");
}
// Output: €1,234.56 (format depends on locale)

{
formatCurrency(price, "GBP", "en-GB");
}
// Output: £1,234.56

formatNumber

Formats a number with a specific number of decimal places, adhering to locale-specific grouping separators.

Syntax:

formatNumber(value, [decimalPlaces], [locale])

Parameters:

  • value (required): The number to format.
  • decimalPlaces (optional): The number of decimal places to display (default: 2).
  • locale (optional): The BCP 47 language tag for formatting (default: your browser's locale).

Examples:

{
formatNumber(1234.567);
}
// Output: 1,234.57

{
formatNumber({ score }, 0);
}
// Output: 1,235

formatDate

Formats a date value into a human-readable string.

Syntax:

formatDate(value, [dateStyle], [locale])

Parameters:

  • value (required): The date to format. Accepts an ISO string or a timestamp.
  • dateStyle (optional): The length/style of the date. Accepted values: "full", "long", "medium", "short" (default: "short").
  • locale (optional): The BCP 47 language tag for formatting (default: your browser's locale).

Examples:

{
formatDate({ submissionDate });
}
// Output: 10/24/2023

{
formatDate("2024-01-15", "long");
}
// Output: January 15, 2024

format (Smart Format)

A flexible wrapper function that automatically detects and applies the appropriate format based on a provided type parameter.

Syntax:

format(value, formatType, [...args])

Parameters:

  • value (required): The value to format.
  • formatType (required): The type of formatting to apply. Accepted values: "currency", "percent", "date", "number".
  • ...args (optional): Additional configuration arguments specific to the chosen formatType.

Examples:

{
format({ amount }, "currency");
} // Output: $1,234.56
{
format(0.75, "percent");
} // Output: 75%

1. Calculated Values (Background Computations)

Calculated Values allow you to define custom form variables that are updated dynamically as the user fills out the form.

Best Practice: Keep Math and Formatting Separate Formatting functions return strings (e.g., "$1,200.00"). Once a number is formatted, you can no longer perform math on it. Therefore, it's highly recommended to use two separate calculated values: one for the raw mathematical logic, and one for the display string.

"calculatedValues": [
{
"name": "rawTotal",
"expression": "{basePrice} * {quantity}"
},
{
"name": "formattedTotal",
"expression": "formatCurrency({rawTotal})"
}
]

You can now safely pipe {formattedTotal} into your titles and UI elements, while keeping {rawTotal} intact for conditional logic (e.g., visibleIf: "{rawTotal} > 1000").

2. Expression Questions (Read-Only Fields)

Expression questions ("type": "expression") calculate their values based on an expression and display the result to the respondent. They are perfect for showing subtotals or summaries.

{
"type": "expression",
"name": "totalDisplay",
"title": "Your Order Total",
"expression": "formatCurrency({subtotal} + {tax})"
}

3. Text Piping (Dynamic Titles)

You can inject formatted values directly into titles, descriptions, and HTML elements using curly braces {}. The SurveyJS engine evaluates the expression inside the braces before rendering.

Your total is: {formatCurrency({totalAmount})}

4. Auto-setting Values (setValueExpression & defaultValueExpression)

You can use formatting functions when calculating default values or conditionally updating fields based on user input.

{
"type": "text",
"name": "displayScore",
"defaultValueExpression": "formatNumber({rawScore}, 1)"
}

Example: Dynamic Order Form

The following JSON schema demonstrates a practical implementation of formatting functions within an order form. It uses calculatedValues to perform the math safely and formatCurrency in an Expression question to present the data cleanly.

!

{
"pages": [
{
"name": "orderPage",
"elements": [
{
"type": "text",
"name": "basePrice",
"title": "Base Price",
"defaultValue": 99.99
},
{
"type": "radiogroup",
"name": "country",
"title": "Select Country",
"choices": [
{ "value": "US", "text": "United States" },
{ "value": "UK", "text": "United Kingdom" },
{ "value": "DE", "text": "Germany" }
]
},
{
"type": "expression",
"name": "taxAmount",
"title": "Tax Amount (Rate: {formattedTaxRate})",
"expression": "{basePrice} * {taxRate}"
},
{
"type": "expression",
"name": "formattedTotal",
"title": "Final Formatted Total",
"expression": "formatCurrency({basePrice} + {taxAmount})"
}
]
}
],
"calculatedValues": [
{
"name": "taxRate",
"expression": "iif({country} = 'US', 0.08, iif({country} = 'UK', 0.20, 0.19))"
},
{
"name": "formattedTaxRate",
"expression": "format({taxRate}, 'percent')"
}
]
}

Pro Tips

  • Logical Conditionals: Use iif() with formatting functions for conditional formatting.
    {
    iif({ score } >= 50, formatCurrency({ score }), "N/A");
    }
  • Order of Operations: Apply the formatting function as the outermost wrapper so that the math resolves before the format string is applied.
    // Correct
    {
    formatCurrency({ price } * { quantity });
    }
    // Incorrect (will result in NaN)
    {
    formatCurrency({ price }) * { quantity };
    }

Math Functions

Perform basic mathematical operations on question values.

sum, max, min, avg

These functions take a list of numbers and calculate their combined result:

  • sum(...): Returns the sum of passed numbers.
  • max(...): Returns the highest number.
  • min(...): Returns the lowest number.
  • avg(...): Returns the average of the numbers.

round

Rounds a number to a specified number of decimal places. If precision is omitted, it rounds to the nearest whole number.

  • Syntax: round(num, precision?)
  • Example: "round(1.005, 2)" (Returns 1.01)

trunc

Truncates a number to a specified number of decimal places by simply chopping off the remaining digits without rounding.

  • Syntax: trunc(num, precision?)
  • Example: "trunc(2.175, 1)" (Returns 2.1)

Array & Matrix Functions

These functions are highly useful for calculating values inside repetitive fields, such as Multi-Select Matrices, Dynamic Matrices, or Dynamic Panels.

sumInArray

Calculates the sum of numbers from a specific column or field inside a matrix. You can optionally apply a filter to only include certain rows.

  • Syntax: sumInArray(question, dataFieldName, filter?)
  • Example: "sumInArray({matrixdynamic}, 'total', {categoryId} = 1)"

maxInArray & minInArray

Finds the maximum or minimum number in a specific column of a matrix.

  • Example: "minInArray({matrixdynamic}, 'quantity', {quantity} > 0)"

avgInArray

Calculates the average of numbers in a specific column of a matrix.

  • Example: "avgInArray({matrixdynamic}, 'quantity', {quantity} > 0)"

countInArray

Counts the total number of rows in a matrix where a specific column has been answered.

  • Syntax: countInArray(question, dataFieldName, filter?)
  • Example: "countInArray({matrixdynamic}, 'quantity', {quantity} > 0)"

Display & Property Functions

displayValue

Returns the human-readable display text of a question rather than its underlying value (e.g., the text of a dropdown choice instead of its ID). You can also pass a specific value to look up its corresponding display text.

  • Syntax: displayValue(questionName, value?)
  • Example: "displayValue('my-dropdown-question', 5)"

:::info Dynamic Evaluation Note If you use displayValue inside a dynamic value expression (setValueExpression), you must also specify a setValueIf condition. Because you are passing the question name as a text string, the form won't automatically know to recalculate the expression when that question changes. Providing a setValueIf trigger ensures the value updates correctly. :::

propertyValue (Obsolete)

Note: This function is obsolete. To access property values, you should use the dollar sign ($) prefix instead.


Operators

OperatorDescriptionExpression Example
emptyReturns true if the value is undefined or null."{q1} empty"
notemptyReturns true if the value is different from undefined and null."{q1} notempty"
|| / orCombines two or more conditions and returns true if any of them is true."{q1} empty or {a2} empty"
&& / andCombines two or more conditions and returns true if all of them are true."{q1} empty and {a2} empty"
! / negateReturns true if the condition returns false, and vice versa.!{q1}
<= / lessorequalCompares two values and returns true if the first is less or equal to the second."{q1} <= 10"
>= / greaterorequalCompares two values and returns true if the first is greater or equal to the second."{q1} >= 10"
== / equalCompares two values and returns true if they are loosely equal (that is, their type is disregarded)."{q1} = 10"
!= / <> / notequalCompares two values and returns true if they are not loosely equal."{q1} != 10"
< / lessCompares two values and returns true if the first is less than the second."{q1} < 10"
> / greaterCompares two values and returns true if the first is greater than the second."{q1} > 10"
+Adds up two values."{a1} + {q2}"
-Subtracts the second value from the first."{q1} - {q2}"
*Multiplies two values."{a1} * {q2}"
/Divides the first value by the second."{q1} / {q2}"
%Returns the remainder of the division of the first value by the second."{q1} % {q2}"
^ / powerRaises the first value to the power of the second."{a1} ^ {q2}"
*= / contains / containCompares two values and returns true if the first value contains the second value within it."{q1} contains 'abc'"
notcontains / notcontainCompares two values and returns true if the first value doesn't contain the second value within it."{q1} notcontains 'abc'"
anyofCompares a value with an array of values and returns true if the value is present, or compares two arrays and returns true if the first array includes any value from the second."{q1} anyof ['value1', 'value2', 'value3']"
noneofCompares a value with an array of values and returns true if the value is absent, or compares two arrays and returns true if the first array includes none of the values from the second."{q1} noneof ['value1', 'value2', 'value3']"
allofCompares two arrays and returns true if the first array includes all values from the second."{q1} allof ['value1', 'value2', 'value3']"
#Disables type conversion for a referenced value (e.g., string values "true", "false", "123" won't be converted to corresponding Boolean and numeric values)."{#q1}"

Advanced

Custom Expression Functions

Endatix Hub supports custom expression functions, allowing you to programmatically extend the system for unique use cases.

Compatibility with SurveyJS

Endatix Hub is powered by the SurveyJS form engine. While all standard SurveyJS expressions are supported, Endatix Hub includes exclusive functions - such as anyAnswered and anyAnsweredByPrefix - that may not be compatible with standalone SurveyJS implementations.

Form more information refer to the official SurveyJS Conditional Logic documentation.