Docs
Validation
Expression-based Validation

Expression-based Validation

When using expressions to validate form inputs, you must provide a JavaScript expression (opens in a new tab) that resolves to a boolean value (either true or false).

Defining an expression-based validator

The basic structure of an expression-based validator looks as follows:

"validators": [
  {
    "type": "js_expression",
    "failsWhenExpression": "isEmpty(myValue) && age <= 14",
    "message": "Patient selected is pediatric. Please provide relationship information."
  }
]
  • type: Must be js_expression.
  • failsWhenExpression: A JavaScript expression that returns a boolean value.
  • message: The error message that gets shown under your form input when the condition provided in your failsWhenExpression evaluates to true.

Going through the example schema provided above, the failsWhenExpression evaluates the following expression: isEmpty(myValue) && age <= 14. This means this input will fail validation if both isEmpty(myValue) and age <= 14 are true. More specifically, this input will fail validation if the question does not get answered and if the age value provided to the form is less than or equal to 14. When this condition evaluates to true, the message value provided gets rendered as an error message under the input. Additionally, the input gets flagged with styling that communicates that it has been fed an invalid value.

Expression-based validators can leverage expression helper functions and data sources, enabling you to create arbitrarily complex validations.

Below is an example of expression-based validation taken from a Cervical Cancer Screening form:

Expression-based validation

The following is a snippet taken from its schema:

{
  "type": "obs",
  "label": "HIV Status",
  "isExpanded": "true",
  "questions": [
    {
      "label": "What is your current HIV status?",
      "id": "hivStatus",
      "type": "obs",
      "required": "true",
      "historicalExpression": "_.isEmpty(HD.getObject('prevEnc').getValue('9e4d6436-4040-46a3-a0ae-6dbc0acfe593')) ? undefined : HD.getObject('prevEnc').getValue('9e4d6436-4040-46a3-a0ae-6dbc0acfe593')",
      "questionOptions": {
        "rendering": "select",
        "concept": "9e4d6436-4040-46a3-a0ae-6dbc0acfe593",
        "answers": [
          {
            "concept": "a896f3a6-1350-11df-a1f1-0026b9348838",
            "label": "HIV positive"
          },
          {
            "concept": "a896d2cc-1350-11df-a1f1-0026b9348838",
            "label": "HIV negative"
          },
          {
            "concept": "a899b50a-1350-11df-a1f1-0026b9348838",
            "label": "Unknown"
          }
        ]
      },
      "validators": []
    },
    {
      "label": "Are you currently in HIV care or under ART?",
      "id": "currentlyOnArt",
      "type": "obs",
      "questionOptions": {
        "rendering": "select",
        "concept": "a8afba58-1350-11df-a1f1-0026b9348838",
        "answers": [
          {
            "concept": "a899b35c-1350-11df-a1f1-0026b9348838",
            "label": "Yes"
          },
          {
            "concept": "a899b42e-1350-11df-a1f1-0026b9348838",
            "label": "No"
          }
        ]
      },
      "validators": [
        {
          "type": "js_expression",
          "failsWhenExpression": "isEmpty(myValue) && hivStatus == 'a896f3a6-1350-11df-a1f1-0026b9348838'",
          "message": "Please indicate whether the client is currently in HIV care or under ART"
        }
      ],
      "hide": {
        "hideWhenExpression": "isEmpty(hivStatus) || hivStatus != 'a896f3a6-1350-11df-a1f1-0026b9348838'"
      }
    }
  ]
}
ℹ️

This question assumes that only HIV Positive clients are expected to be engaged in active HIV care.

A lot is going on in this example. Let us attempt to break it down step by step. The first question (id hivStatus) seeks to establish the client's HIV status. The next question (id currentlyOnArt) asks whether the client is actively receiving HIV care. Note that this second question also has validation enabled.

{
  "validators": [
    {
      "type": "js_expression",
      "failsWhenExpression": "isEmpty(myValue) && hivStatus == 'a896f3a6-1350-11df-a1f1-0026b9348838'",
      "message": "Please indicate whether the client is currently in HIV care or under ART"
    }
  ]
}

We see that the type of the validator is js_expression, which indicates that we're using JavaScript expression-based validation. The failsWhenExpression property takes a JavaScript expression as its value. The expression evaluates two conditions:

  1. That this question (id currentlyOnArt) gets answered. isEmpty(myValue) returns true if this question does not get answered, and false otherwise.
  2. That the previous question (id hivStatus) has the answer with the value a896f3a6-1350-11df-a1f1-0026b9348838. This value corresponds to the value labelled HIV Positive.

This is how this plays out in practice:

Expression-based validation

When the current HIV status gets set to HIV Positive and the next question is left unanswered, the validation kicks in and displays the contents of the message property as a validation error.

More examples

1. Using moment (opens in a new tab) to validate encounter dates

"validators": [
  {
    "type": "js_expression",
    "failsWhenExpression": "(new moment(encDate)).isAfter((new moment(myValue)), 'day') || (new moment(encDate)).isSame((new moment(myValue)), 'day')",
    "message": "Return to clinic date should be greater than the encounter date."
  }
]

2. Using arrayContains and isEmpty helpers

"validators": [
  {
    "type": "js_expression",
    "failsWhenExpression": "isEmpty(myValue) && arrayContains(['7c6f0599-3e3e-4f42-87a2-2ce66f1e96d0'], patientReferrals)",
    "message": "Patient referred for DC. Medication pick-up date should be required."
  }
]

3. Using multiple logical operators (|| and &&)

"validators": [
  {
    "type": "js_expression",
    "failsWhenExpression": "(isEmpty(onTbTreatment) || !arrayContains(['a899b35c-1350-11df-a1f1-0026b9348838'], onTbTreatment)) && !isEmpty(myValue)",
    "message": "You indicated that the patient is not on Tuberculosis treatment, therefore a TB treatment start date should not be provided."
  }
]

4. Leveraging values from an injected data source

ℹ️

In this example, the sex value is obtained from a Patient data source.

"validators": [
  {
    "type": "js_expression",
    "failsWhenExpression": "isEmpty(myValue) && sex === 'M' && !isEmpty(childrenAgedBelow19) && childrenAgedBelow19 === 'a899b35c-1350-11df-a1f1-0026b9348838'",
    "message": "Please provide a reason for failure to conduct elicitation."
  }
]