Component properties
Last updated
Last updated
Every UI component exposes a set of properties that influence its behavior and layout. These properties are managed in the visual property editor and often do not require any code. In case a more dynamic behavior is required, you can use JavaScript to achieve that. This section introduces you to the key concepts and enables you to create highly dynamic, reactive component configurations.
Every component property has a specific type, which determines what kind of values are permitted for that property, and which UI element is offered in the properties panel to modify it. Examples for such property types are:
The Disabled
property of a button is boolean
, and hence is supposed to be either true
or false
. It is configurable via a switch element in the properties panel.
The On change run
property of a text-input is an action
, and hence is supposed to contain an action that should be triggered when the particular event occurs. It is configurable via a select dropdown to choose any of the existing actions.
The Label
property of a text-input is a stringWithJs
, which means that it can be any text with potentially embedded JavaScript snippets. It is configurable via a text-input field in the properties panel.
The Table data
property of a table is a js
property. It is supposed to contain a JavaScript expression which evaluates to the final value of the property. It is configurable via a code-input field in the properties panel.
You can find a full list and description of all types in the reference below.
Many properties do not allow to apply dynamic configuration logic via JavaScript with their default input UI element. The best example for that is the boolean
type, which uses a simple switch-input to set the property value. This input is only able to set that property statically to the value true
or false
. There are many scenarios where the property value should be calculated and updated dynamically though.
For such cases, some property types allow to switch from a static configuration (for example, using a switch) to a dynamic configuration (using JavaScript expressions). Such property transformation is indicated by a small fx
icon around that property:
Clicking on such an fx
icon replaces the default UI element of the property with a code-input field. You can write any valid JavaScript expression into this input, and it will be evaluated dynamically to the final property value.
The JavaScript engine expects that your dynamic expressions evaluate to the same data type that is expected by the particular property. For example, an expression in the code-input for a boolean
property should evaluate to a boolean
value. Otherwise, the preview will show you a type error (see the preview section below)
A valid example of transforming a boolean
property to a dynamic expression may look like this:
You can always switch back to a static property input in case a dynamic JavaScript expression is not required any more. The property will then keep the most recent value that your dynamic expression evaluated to.
Please check out the property reference to find out which property types can be transformed to JavaScript expressions, and which data types they expect.
Certain properties require a JavaScript object or array by default, or are expected to always require dynamic calculation. Examples of such properties are the Table Data
property of the Table component, or the Data
property of the Funnel component. There is no way to, and no real meaning in, defining such properties statically, so their values are managed with a code-input field by default. These code-input fields visually look similar to regular text inputs, but allow you to write JavaScript expressions with all major IntelliSense features out of the box.
Below you see an example of such a JavaScript property with a minimalistic set of table data. You can immediately spot the syntax highlighting, which indicates a code-input field:
Code-input fields and regular text-input fields in the properties panel are usually easily distinguishable. Handling strings however sometimes leads to confusion, because they need to be entered differently. In a regular text-input, you can just enter the desired text without any special syntax. For a code-input field, this will however lead to an error:
Since we are operating in a code-input field, all entered text is treated as a JavaScript expression. In this example, the text Hello
is interpreted as a variable, which is not defined here.
The correct way to enter a string into such input is to enter those as valid JavaScript strings:
Properties that expect regular text for their values are modified via simple text-input fields. This avoids the necessity to write them as JavaScript strings every time (see previous section). However, these texts should contain dynamic content as well in many situations. For that reason, we allow to embed JavaScript expressions in such text fields using a mustache-like syntax. You can enter regular text, but embed expressions by wrapping them in doubly-curly braces as follows:
You can embed as many expressions as you want, and they will all be replaced with the evaluated value at runtime.
The runtime expects that these embedded expressions evaluate to a value of type string. In case a value with a different type is met, it performs its best effort to convert it to something textual, but a warning will be shown to you:
We have learned so far about two ways to dynamically calculate property values:
JS-properties, that expect a JavaScript expression
Text-properties, that allow to embed expressions with a mustache-like syntax
In both cases, you certainly would like to understand whether your code works exactly as you expect, and see what it evaluates to in result. As soon as your input contains some code, the application will render a small preview of what your input will evaluate to. In case there is no code in your text-input yet, such preview is unnecessary and will not be shown:
Syntax errors, and any errors during evaluation of your expressions, will be shown to you as well, to support you in fixing such errors quickly:
On top of highlighting syntax and runtime errors, our engine checks for compatibility of the evaluated value of your expressions with the expected data type of the property that you are editing. For example, if you convert the switch of a Disabled
property to a dynamic JavaScript expression, this expression should evaluate to a boolean
value. If that is not the case, you will see an error hinting you to that fact:
Similar to the recommendation that embedded expressions in text-inputs should evaluate to a string value, we recommend to follow these type recommendations precisely, and apply explicit conversions where necessary.
The JavaScript runtime exposes specific variables in every code snippet that you write in the component properties. These variables expose evaluated properties of the UI components, actions, state variables and meta-data like the authenticated user (read more). These exposed properties are usually called "component API", "action API" and "state API" respectively.
For example, every UI component is exposed by its name as a separate variable. If you have a text-input component called productName
in your app, this will be exposed as a global variable called productName
in any code snippet as well. By using the value
property of that object, you can access the current text in that input, and use it in your calculations:
Especially when dealing with component properties that change frequently (like the value
of an input), the expressions using these properties should ideally update automatically whenever such change occurs. Using stale data in these expressions is clearly not desired.
For that reason, any property of any variable that you are using in JavaScript expressions behaves like an Observable. Whenever even a single used variable changes, the entire expression will be re-evaluated by the runtime. This might in turn trigger cascaded updates of other properties, until the affected part of the dependency graph is processed:
Let's unfold this example step by step:
productName
is a regular text-input. Its value should be mirrored in the other two input fields.
nameCopy1
receives the dynamic expression {{productName.value}}
. This registers a dependency between its value and the value
property of productName
nameCopy2
receives the slightly different expression {{nameCopy1.value}}
. This registers a dependency between its value and the value
property of nameCopy1
Whenever the value of the input-field productName
changes - regardless whether this change happens programmatically or via UI interaction of the user - the runtime follows a specific chain of logic. Let us assume for now that the value
of the productName
field has been changed to "Macbook":
All properties depending on productName.value
are looked up from the internally managed dependency graph. This resolved to the value
property of nameCopy1
.
The property configuration of that value
property, i.e. {{productName.value}}
, is re-evaluated by the runtime, and evaluates to "Macbook".
Due to this new change of the value
property of nameCopy1
, the runtime performs another lookup for any property that depends on nameCopy1.value
in the dependency graph. This recursive lookup resolved to the value
property of nameCopy2
.
The property configuration of that value
property is nameCopy1.value
, which will be re-evaluated as well. This evaluation leads to "Macbook" as well.
The updated calculated values for the value
prop of productName
, nameCopy1
and nameCopy2
are dispatched to the application state and rendered by each affected component.
The same mechanism is applied every time an exposed property of a component, action or state variable is changed. You can therefore rely on your JavaScript expressions always being evaluated to the latest value.
There are only two possible triggers for a dynamic property configuration to be re-evaluated:
The configuration, i.e. the code, is changed in the properties panel or by another collaborator
An exposed property, that is used in the code, has changed
Expressions like {{new Date()}}
or {{Math.random()}}
do not have any automated re-evaluation logic and might not behave as you expect.
The automatic tracking and re-evaluation of dependencies is a powerful feature, however it has a threat when used incorrectly: dependency cycles. Let's imagine the most basic example of such a cycle:
The value
prop of text-input component A
contains: {{B.value}}
The value
prop of text-input component B
contains: {{A.value}}
If either of the two values changes, we are entering an infinite update cycle. The runtime is able to detect such cycles and display a warning in such cases:
The outcome for the affected expressions / properties is not deterministic. It is highly recommended to immediately resolve any dependency cycle that occurs.
Using JavaScript expressions in component properties is a powerful tool for creating dynamic and reactive UI behavior. In case your use case requires larger pieces of code, the input fields in the properties panel may be inconvenient though due to their small size. In this case, you have the ability to expand the input to a extended code editor with a large preview area. Use the expand-icon in the code input field to open the expanded code editor:
The expanded editor allows you to:
Write larger expressions comfortably
Inspect complex previews
The following table lists all component property types and their main characteristics:
This value will be rendered correctly as "1 + 1 equals: 2", but it is still recommended that you take care of the conversion to string explicitly, to not rely on our implicit conversion mechanism.
Property type | Standard UI input | Transformable to JS | Embedded JS | Example property |
---|---|---|---|---|
js
code input
Data
of a Table
stringWithJs
text input
Label
of a TextInput
boolean
switch
Disabled
of a Button
string
case by case
Label alignment
of a TextInput
array
code input
Options
of a Select
color
text input / color picker
Value color
in a TextInput
action
Dropdown select
On click run
action of Button