Dynamic Properties
A "dynamic" property is a JSON object that specifies how a parameter property should be affected by other parameter properties. See supported dynamic properties.
Dynamic properties may depend on properties of other parameters or properties of the parameter itself. These dependencies must not form a cycle. For examples, see cycle examples.
Dynamic Property Expression Objects
A dynamic property expression is a JSON object with two keys:
- if (
array
, required) an array of dynamic control clauses. - default (dynamic-property-type, optional) the value to use for the dynamic property if control expressions truth cannot be determined (for example: if a dependent parameter value is a Published Parameter), or if all expressions are false.
A dynamic property value will be evaluated as if it were a list of conditions and effects with the pseudocode:
try
{
if (if0) { /* then0 value */ }
else if (if1) { /* then1 value */ }
//...
else if (ifN) { /* thenN value */ }
else { /* default value */ }
}
catch (unknownCondition)
{
/* default value */
}
"if"
Array
The "if"
property is a JSON array of Clause objects.
The order of the "if"
array is important: Only the first clause with a condition that evaluates to true
will be enacted. All subsequent clauses will not be considered.
If a clause that is under consideration is unable to be evaluated to true
or false
, then the value of the dynamic property will fall back to the value specified in "default"
. This can happen if a parameter value is published parameter or an expression that cannot be evaluated at the time it is displayed to the user.
If no clauses are enacted, then the "default"
value for the dynamic property will be enacted.
Parameter Dynamic Property Example:
"parameters": [
{
"name": "TEXT_B",
"type": "text",
"prompt": "Enter Text",
"valueType": "string",
"visibility": {
"if": [ /*...clauses...*/ ]
}
}
]
Clause
A Clause includes a condition expression object and a ("then"
)[#then-value] value. If a condition evaluates to true
, then its associated "then"
value will be enacted.
Each Clause object may only contain 1 condition expression object, denoted by a field name that begins with "$"
. If no condition expression object is present, then the condition will be considered true
.
Each Clause object may also contain 1 parameter state object with the property name "then"
. If no "then"
property is present, then the default value for the dynamic property will be enacted.
Clause Example:
{
"$operator": { /*...condition expression...*/ },
"then": /* property value */
}
"$condition"
Expression Object
A condition object is a logical expression composed of an operator and its arguments.
The logical operator is given by the JSON field name for the condition object.
Condition Expression Example:
{
"$equals": {
"parameter": "CONTROLLING_PARAMETER",
"value": "CONDITIONAL_VALUE"
}
}
In the above example, the logical operator is "$equals"
. This operator has two arguments: "parameter"
, and "value"
.
Condition arguments can themselves be condition expression objects. For example:
Nested Condition Example:
{
"$anyOf": [
{
"$equals": {
"parameter": "CONTROLLING_PARAMETER",
"value": "CONDITIONAL_VALUE"
}
},
{
"$equals": {
"parameter": "CONTROLLING_PARAMETER",
"value": "OTHER_CONDITIONAL_VALUE"
}
}
]
}
Certain condition operators, such as "$anyOf"
and "$allOf"
, are specified as an array of condition expressions instead of a single arguments object.
The list of supported condition operators can be found here.
"then"
Value
A "then"
value is a JSON value that will be applied to a dynamic property when its associated condition expression evaluates to true
.
A "then"
value must be the correct type for the dynamic property. For example, a "then"
value for the "visibility"
dynamic property must be a boolean
or a string
containing one of the special values ["visibleEnabled"
, "visibleDisabled"
, "hiddenEnabled"
], and the "then"
value for the "setValue"
dynamic property must be a string
.
"then"
Example:
"expand": {
"if": [{
"$not": { $isEnabled": { "parameter": "OTHER_PARAMETER" } }
"then": false // <--- collapses this parameter when OTHER_PARAMETER is disabled
}]
}
The list of supported parameter state fields can be found here.
"default"
Value
A "default"
value specifies the value for a dynamic property if one of the conditions under consideration in the "if"
array is unable to be evaluated to true
or false
, i.e. the condition evaluates to unknown
.
If "default"
is not specified, then the default value for the dynamic property will be used.
The type of value for "default"
must match the type of the dynamic property.
"default"
Example:
"setValue": {
"if": [
{
"$equals": { "parameter": "OTHER_PARAMETER", "value": "A" } }
"then": "MODE_A"
},
{
"then": "MODE_B"
},
],
"default": {
"value": "AUTOMATIC_MODE",
"override": false // false means that we will only set the value
} // if the user has not input a value themselves.
} // Defaults should usually have "override": false
In the above example, the following behaviour will occur:
- if the value of "OTHER_PARAMETER"
is unknown (eg. a published parameter or expression), then value will be set to "AUTOMATIC_MODE"
unless the user has entered a value. Otherwise
- if the value of "OTHER_PARAMETER"
is "A"
, then value will be set to "MODE_A"
; otherwise
- the value will be set to "MODE_B"
.
Pseudocode:
try
{
if (OTHER_PARAMETER.value == "A")
{
value = "MODE_A";
}
else
{
value = "MODE_B";
}
}
catch (ValueUnknownException)
{
if (userHasNotInputAValue())
{
value = "AUTOMATIC_MODE";
}
}
Supported Condition Expression Operators
Condition Expression Specification Overview
- $equals
true
if target parameter value matches a value exactly - $matchesRegex
true
if target parameter value matches a regular expression - $isEnabled
true
if target parameter is enabled - $allOf
true
if all contained expressions aretrue
- $anyOf
true
if any contained expression istrue
- $not
true
if contained expression isfalse
$equals
Type: object
Object Keys:
* parameter (string
, conditionally required): the parameter that will have its value compared to another value .
* column (string
, conditionally required): the name of the column in same row that will have its value compared to another value ("table"
columns only).
* value (string
, required): the value that will be compared against the target parameter value.
Expression evaluates to true
if the target parameter value is exactly the same as expression "value"
. Otherwise if target parameter value is unknown (published parameter, attribute value, etc.), then the expression evaluates to unknown
. Otherwise false
.
Either "parameter"
or "column"
is required. Object must not contain both. "column"
is only valid for parameters specified in a array of "table"
columns.
$equals Example:
{
"$equals": {
"parameter": "CONTROLLING_PARAMETER",
"value": "VALUE_THAT_MAKES_IT_TRUE" // case-sensitive: would not match "value_that_makes_it_true"
}
}
$matchesRegex
Type: object
Object Keys:
* parameter (string
, conditionally required): the parameter that will have its value matched against a regular expression
* column (string
, conditionally required): the name of the column in same row that will have its value matched against a regular expression ("table"
columns only).
* regex (string
, required): the regular expression that will be used to match against the target parameter value
Expression evaluates to true
if the entire target parameter value matches the regular expression. Otherwise if target parameter value is unknown (published parameter, attribute value, etc.), then the expression evaluates to unknown
. Otherwise false
.
Either "parameter"
or "column"
is required. Object must not contain both. "column"
is only valid for parameters specified in a array of "table"
columns.
Regular expression syntax is PCRE.
$matchesRegex Example:
{
"$matchesRegex": {
"parameter": "ALPHANUMERIC_REGEX_PARAMETER",
"regex": "[a-zA-Z0-9]+" // would match "123AtoZ", but would not match "123 A to Z"
}
}
$isEnabled
Type: object
Object Keys:
* parameter (string
, conditionally required): the name of the parameter that will be checked
* column (string
, conditionally required): the name of the column value in same row that will be checked ("table"
columns only).
Expression evaluates to true
if parameter exists and is enabled, otherwise false
.
Either "parameter"
or "column"
is required. Object must not contain both. "column"
is only valid for parameters specified in a array of "table"
columns.
$isEnabled Example:
{
"$isEnabled": {
"parameter": "OTHER_PARAMETER_NAME",
}
}
$allOf
Type: array
Array Values:
* condition expression (object
): an object with exactly 1 condition expression operator.
Requirements: * Must contain at least 2 conditions expression objects.
The array of condition expressions will be evaluated in order.
- If an expression evaluates to false
, then evaluation is short-circuited and the $allOf
expression evaluates to false
.
- Otherwise if 1 or more expressions evaluates to unknown
, then the $allOf
expression evaluates to unknown
.
- Otherwise, the $allOf
expression evaluates to true
.
$allOf Example:
{
"$allOf": [
{
"$equals": {
"parameter": "CONTROLLING_PARAMETER_0",
"value": "MUST_BE_THIS"
}
},
{
"$equals": {
"parameter": "CONTROLLING_PARAMETER_1",
"value": "MUST_ALSO_BE_THIS"
}
}
]
}
$anyOf
Type: array
Array Values:
* condition expression (object
: an object with exactly 1 condition expression operator.
Requirements: * Must contain at least 2 conditions expression objects.
The array of condition expressions will be evaluated in order.
- If an expression evaluates to true
, then evaluation is short-circuited and the $anyOf
expression evaluates to true
.
- Otherwise if 1 or more expressions evaluates to unknown
, then the $anyOf
expression evaluates to unknown
.
- Otherwise the $anyOf
expression evaluates to false
.
$anyOf Example:
{
"$anyOf": [
{
"$equals": {
"parameter": "CONTROLLING_PARAMETER",
"value": "COULD_BE_THIS"
}
},
{
"$equals": {
"parameter": "CONTROLLING_PARAMETER",
"value": "OR_COULD_BE_THIS"
}
}
]
}
$not
Type: object
Object Keys:
* [$conditionExpressionOperator] ( object
, required): the logical expression that will be negated.
Requirements:
* The contained condition expression can be any condition expression except for a $not
expression.
* There must be exactly one contained condition expression.
- If the contained expression evaluates to
false
, then the$not
expression evaluates totrue
. - Otherwise if the contained expression evaluates to
unknown
, then the$not
expression evaluates tounknown
. - Otherwise the
$not
expression evaluates tofalse
.
$not Example:
{
"$not": {
"$equals": {
"parameter": "CONTROLLING_PARAMETER",
"value": "NOT_THIS_VALUE"
}
}
}
Full Example:
"name": "EXAMPLE_PARAMETER",
"visibility": {
"if": [{
"$equals": {
"parameter": "OTHER_PARAMETER",
"value": "DISABLE"
},
"then": "visibleDisabled"
}]
},
"setValue": {
"if": [
{
"$not": { "$isEnabled": { "parameter": "EXAMPLE_PARAMETER" } },
"then": ""
},
{
"$anyOf": [
{
"$equals": {
"parameter": "OTHER_PARAMETER",
"value": "DO_X_AND_SET_EXAMPLE_TO_Z"
}
},
{
"$equals": {
"parameter": "OTHER_PARAMETER",
"value": "DO_Y_AND_SET_EXAMPLE_TO_Z"
}
}
],
"then": "Z"
}
]
}
Which is equivalent to the following pseudocode:
// process "enable"
try
{
if (equals(valueOf("OTHER_PARAMETER"), "DISABLE"))
{
example.setActive(false);
example.setVisible(true);
}
else
{
example.setActive(true);
example.setVisible(true);
}
}
catch (ValueUnknownException)
{
example.setActive(true);
example.setVisible(true);
}
// process "value"
try
{
if (!isEnabled("EXAMPLE_PARAMETER"))
{
example.setValue("");
}
else if ( equals(valueOf("OTHER_PARAMETER"),
"DO_X_AND_SET_EXAMPLE_TO_Z")
|| equals(valueOf("OTHER_PARAMETER"),
"DO_Y_AND_SET_EXAMPLE_TO_Z"))
{
parameter.setValue("Z");
}
// else default "value" is undefined, i.e. don't change anything
}
catch(ValueUnknownException)
{
// default "value" is undefined, i.e. don't change anything
}
Cycle Examples
A cycle is any chain of property dependencies where a property somehow depends on itself.
For purposes of validity, it does not matter whether the cycle would cause an infinite loop of changes or not. If a dynamic property mentions a property in its conditions which in any way references the original dynamic property, then the dynamic property definition is invalid.
Value Depends On Enabledness, Which Depends On Value
A parameter's "setValue"
dynamic property could depend on its enabledness, but then its "visibility"
dynamic property's enabledness must not depend on the parameter's value.
Valid
"name": "EXAMPLE",
"visibility": {
"if": [
{
"$equals": {
"parameter": "OTHER_PARAMETER",
"value": "DISABLE"
},
"then": "visibleDisabled"
}
]
},
"setValue": {
"if": [
{
"$not": { "$isEnabled": { "parameter": "EXAMPLE_PARAMETER" } },
"then": ""
}
]
}
Invalid
"name": "EXAMPLE",
"visibility": {
"if": [
{
"$equals": {
"parameter": "OTHER_PARAMETER",
"value": "DISABLE"
},
"then": "visibleDisabled"
},
{
"$equals": {
"parameter": "EXAMPLE", // introduces dependency own our own value
"value": "some value"
},
"then": "visibleDisabled"
}
]
},
"setValue": {
"if": [
{
"$not": { "$isEnabled": { "parameter": "EXAMPLE_PARAMETER" } },
"then": ""
}
]
}
Enabledness Depends On Value, Which Depends On Enabledness
A parameter's "visibility"
dynamic property could depend on its value, but then its "setValue"
dynamic property must not depend on the parameter's enabledness. (Note: having a parameter's enabledness depend on its value is probably not a good pattern for usability)
Valid
"name": "EXAMPLE",
"visibility": {
"if": [
{
"$equals": {
"parameter": "EXAMPLE",
"value": "I should be disabled"
},
"then": "visibleDisabled"
}
]
},
"setValue": {
"if": [
{
$equals": {
"parameter": "OTHER_PARAMETER",
"value": "DISABLE_EXAMPLE"
},
"then": "I should be disabled"
}
]
}
Invalid
"name": "EXAMPLE",
"visibility": {
"if": [
{
"$equals": {
"parameter": "EXAMPLE",
"value": "I should be disabled"
},
"then": "visibleDisabled"
}
]
},
"setValue": {
"if": [
{
$equals": {
"parameter": "OTHER_PARAMETER",
"value": "DISABLE_EXAMPLE"
},
"then": "I should be disabled"
},
{
$isEnabled": {
"parameter": "EXAMPLE" // introduces dependency on our own enabledness
},
"then": "some value"
}
]
}
Property Depends On Itself
A parameter's dynamic property cannot depend on its own value. An example with "setValue"
:
Invalid
"name": "EXAMPLE",
"setValue": {
"if": [
{
"$equals": {
"parameter": "EXAMPLE", // introduces dependency on our own value
"value": "A"
},
"then": "B"
}
]
}
Parameter A Depends On Parameter B, Which Depends On Parameter A
A parameter's dynamic property cannot depend on another parameter's property that depends on the original property.
Invalid
"parameters": [
{
"name": "A",
"visibility": {
"if": [
{
"$isEnabled": {
"parameter": "B", // depends on B
},
"then": "visibleDisabled"
}
]
}
},
{
"name": "B",
"visibility": {
"if": [
{
"$isEnabled": {
"parameter": "A", // depends on A
},
"then": "visibleDisabled"
}
]
}
}
]
The dependency does not have to be a direct dependency. Any chain of dependencies that forms a cycle is invalid.
"parameters": [
{
"name": "A",
"visibililty": {
"if": [
{
"$equals": {
"parameter": "B", // depends on B value
"value": "DISABLE_A"
},
"then": "visibleDisabled"
}
]
}
},
{
"name": "B",
"setValue": {
"if": [
{
"$isEnabled": {
"parameter": "C", // depends on C enabledness
},
"then": "DISABLE_A"
}
]
}
},
{
"name": "C",
"visibility": {
"if": [
{
"$isEnabled": {
"parameter": "A", // depends on A enabledness
},
"then": "visibleDisabled"
}
]
}
}
]