Filtering
Removing some options from the list
Filtering is a way to remove some of the options from the list. This can be useful if you want to limit which options the user can choose from.
Keep in mind that there are different approaches to making options dynamic:
- You can use dynamics to hide and show entirely different components based on a condition. These components could be bound to the same location in the data model, but have different options. Note that automatic cleanup may unexpectedly remove values from the data model when using this approach. Test thoroughly to ensure form data is not lost when using this method.
- By using dynamic options and passing query parameters to the backend, you can write custom code to generate a different set of options based on those query parameters. This may be useful, but can cause lots of traffic to the backend if the options are frequently changing.
- Using options from the data model you can set up options based on a repeating structure in the data model. In combination with data processing on the backend, this can be a powerful way to create custom options even when dynamic options based on query parameters would be problematic.
Filtering options via the optionFilter
property works with all of the above, and with
plain static options as well. It makes it possible to use a dynamic expression
to filter out options based on the current state of the form.
Configuration
In the example below, the optionFilter
property is set to a dynamic expression that filters out the
option should-be-removed
. Note that the optionFilter
property accepts an expression for which options to keep,
so you should invert the logic to remove an option.
The expression is evaluated for each option, and if it returns true
, the option is kept. All other options are removed.
{
"id": "dropdown-component-id",
"type": "Dropdown",
...
"options": [
{ "value": "should-be-removed", "label": "Should be removed" },
{ "value": "red", "label": "Red" },
{ "value": "blue", "label": "Blue" }
],
"optionFilter": ["notEquals", ["value"], "should-be-removed"]
}
The result of the above configuration will be a dropdown with two options: “Red” and “Blue”.
The value
function
In the example above, the value
function is used to access the value of the current option. This function can be used
with arguments to access other values in the option as well.
["value"]
and["value", "value"]
are equivalent, and will return the value of the current option.["value", "label"]
will return the label of the current option. This label is the text given in thelabel
property of the option, before any text resources are looked up.["value", "description"]
will return the description of the current option, if set.["value", "helpText"]
will return the help text of the current option, if set.
Used alongside options from the data model
When using optionFilter
with options from the data model, the expression will be evaluated for each row in the
repeating structure. This means that if you look up the data model (via the dataModel
function) in the expression,
you will have access to data from the row that the option was fetched from.
If there is a RepeatingGroup
component associated with this repeating structure, the optionFilter
property can also
look up values from the component
function to access data from components inside the repeating group. The return value
from this function will always be null
if the row is hidden using
dynamics in the hiddenRow
property,
even if a lookup with the dataModel
function would return data from the hidden row.
An example using this combination:
{
"id": "choose-pet",
"type": "Dropdown",
...
"source": {
"group": "MyPets",
"label": ["dataModel", "MyPets.Name"],
"value": "MyPets[{0}].Id"
},
"optionFilter": [
"and",
["notEquals", ["dataModel", "MyPets.Name"], null],
["notEquals", ["component", "pet-owned-by-someone-else"], true],
["notEquals", ["value"], "example-cat-id"]
]
}
In this example, the optionFilter
property will filter out all pets that:
- Do not have a name (the path
MyPets.Name
isnull
or an empty string) - Are owned by someone else (the value in the
pet-owned-by-someone-else
component istrue
). In this example, we assume that this component is set up inside aRepeatingGroup
component that is associated with theMyPets
structure. - Have the ID
example-cat-id
. Since thevalue
field is fetched from the pathMyPets[{0}].Id
, the result will be the same as if you wrote["notEquals", ["dataModel", "MyPets.Id"], "example-cat-id"]
.
Example: Filtering duplicate options in a repeating group
In the following animation, a RepeatingGroup
component has been set up with a Dropdown
component inside.
The Dropdown
component has a optionFilter
property that removes options that have already been used in
other rows in the repeating group.
The configuration for this example is as follows:
{
"id": "ingredientType",
"type": "Dropdown",
"textResourceBindings": {
"title": "Ingrediens"
},
"dataModelBindings": {
"simpleBinding": "Ingredients.Type"
},
"optionsId": "foods",
"optionFilter": [
"or",
// Remove those that have been used elsewhere
["not", ["commaContains", ["dataModel", "UsedTypes"], ["value"]]],
// But not if it's the currently chosen ingredient here
["equals", ["component", "ingredientType"], ["value"]]
]
}
A few things to note about the configuration:
- The already used types are stored in a comma-separated list in the
UsedTypes
field in the data model. This field is updated using a data processor that finds all the unique types in theIngredients
array. - If we just checked the
UsedTypes
field against thevalue
of the currentDropdown
component, as soon as an ingredient was chosen, it would be removed from the list of options and would be automatically cleaned up. For this reason, we also check that thevalue
is not equal to the data set in the currentDropdown
component.
UsedTypes
field. For this reason, it is still fully possible to select the same ingredient in multiple rows
in the repeating group if you’re fast enough. When using a method like this you should
also implement validation to catch any duplicates that might slip through.