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 bejs_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 yourfailsWhenExpression
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:
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:
- That this question (id
currentlyOnArt
) gets answered.isEmpty(myValue)
returnstrue
if this question does not get answered, andfalse
otherwise. - That the previous question (id
hivStatus
) has the answer with the valuea896f3a6-1350-11df-a1f1-0026b9348838
. This value corresponds to the value labelledHIV Positive
.
This is how this plays out in practice:
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."
}
]