Our rationale about using jexl can be found here.
Caluma provides a number of variables to use within JEXL expressions. We assume that you're familiar with the expression language itself, so we're not explaining the syntax here.
-
form
refers to the name of the main form of the document. This is useful if you use a question in various forms, where it should behave differently. You could for example write an expression foris_required
like this:form == 'building_permit'
. If you use the quesiton in a form named "building_permit", it would be required, but if you use it in a form named "general_request", it would not be required. -
info
is a data structure containing information about the document and it's form. It's typology is roughly as outlined below. Common access patterns are:info.root.form
- Same asform
, gives the root form's sluginfo.form
- The direct form where the question resides. Could be a row form if the question is within a table, or a FormQuestion's form, etc.info.parent.form
- The form above the current form. May be useful in deeply nested structures.
Note that the info
object also contains further information about othe
questions and answers, but they're explicitly not for use in JEXL expressions,
and may change without notice.
Transforms are used to turn one type of information into another, similar to pipes in the unix shell. They can be chained, and may be parametrized as well.
Here are the available transforms:
answer
: When applied to a question slug, returns the answer to that question in the context of the current document. For example'your-name'|answer
could evaluate to "David". In ais_required
context, you'll need a boolean value, so you could for example use'your-name'|answer == 'Fred'
in theis_required
field of the "birthday" question, so only Fred needs to tell us his birthday. Normally, if the requested question is not found in the form it will throw an exception. However, sometimes it's not necessary to fail so the JEXL works in multiple environments. To allow this we can pass a parameterdefault_value
to the answer transform:'nonexistent-question'|answer('default')
. This parameter declares that the question doesn't have to exist in the form and will be returned if it doesn't exist.mapby
: Extract a nested value from a list. Assuming you have a table of things currently in the fridge, and you want help the user to decide what to cook:'ravioli' in 'fridge-contents'|answer|mapby('food-name')
will tell you whether there are ravioli.debug
: Does not modify the value, but writes the value to the log. This is especially useful when you are exploring the data while building forms or workflows. The log message may appear in different places depending on where it's being run (Browser: console log in the debug tools, Server: System or container logs, might depend on your logging configuration)
Other operators that aren't transforms are also available:
in
: Tells us if a value (left) is contained in a list (right).intersects
: Tells us if two lists have intersecting values. In a way, this is a generalizedin
operator. This can be used for example in multiple choice questions, as follows: To hide questions only relevant for vegetarians or vegans, hide those questions by asking some food choices first, then doing this:'food_choices'|answer intersects ['meat', 'fish']
to see if you should ask specific questions
FormatValidator classes can validate input data for answers, based on rules set on the question.
There are a variety of base FormatValidators ready to use. There is also an extension point for them.
List of built-in base FormatValidators:
- phone-number