# Welcome to Uify

Uify is a collaborative low-code platform to build apps for and together with your operation teams.

Use ready-made, highly configurable components to create modern UIs on top of your databases and APIs. Write Javascript & SQL to create business logic and interact with data. The visual editor is real-time collaborative, enabling you to build together with your colleagues and end-users.

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td><strong>Getting started</strong></td><td>Use our step-by-step guide to launch your first app or check out our quickstart video to learn about the fundamentals of our platform.</td><td><a href="/pages/J6tGbANXpCEkO24kZL0l">/pages/J6tGbANXpCEkO24kZL0l</a></td><td><a href="/files/wW6BGBXrORNkXI3j0bQt">/files/wW6BGBXrORNkXI3j0bQt</a></td></tr><tr><td><strong>Data Management</strong></td><td>Apps are build on some sort of data foundation. Learn how to prepare your data for use in your apps with data management.</td><td><a href="/pages/GC4Y7RHtVEesaKPvRanU">/pages/GC4Y7RHtVEesaKPvRanU</a></td><td><a href="/files/7yPz8WLGE4paB0FT3uYf">/files/7yPz8WLGE4paB0FT3uYf</a></td></tr><tr><td><strong>Integrations</strong></td><td>We continuously add new integrations to our platform to allow you to seamlessly let your data flow between your Uify apps and your existing systems.</td><td><a href="/pages/aBn50HHVSrAYLnbFXzMs">/pages/aBn50HHVSrAYLnbFXzMs</a></td><td><a href="/files/d0zw5II8UDOqrZHbLSFO">/files/d0zw5II8UDOqrZHbLSFO</a></td></tr></tbody></table>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>AI-powered processes with GPT-4</strong></td><td></td><td>We freshly released our OpenAI integration. You can now autogenerate replies to your customer support tickets, call API endpoints based on an email or even classify or tag your product descriptions.</td><td><a href="https://uify.io/post/april-openai-integration-state-inspector-simpler-action-results">Learn more in our updates >></a></td><td><a href="/files/SsR98eterbjgautOUi14">/files/SsR98eterbjgautOUi14</a></td><td><a href="/pages/z2LmCEq9GUDo9CKyKQpE">/pages/z2LmCEq9GUDo9CKyKQpE</a></td></tr><tr><td><strong>Build custom processes around your inbox</strong></td><td> <mark style="color:purple;background-color:purple;">Beta</mark> </td><td>Most processes start and end with an email. That's way your processes should be built around around your inbox and not in a siloed tool. Our newly released GMail integration allows you to receive, process and send emails right out of Uify. </td><td><a href="mailto:support@uify.io">Contact us to get access</a></td><td><a href="/files/wiXDuiQ29xuHoPxwxGFw">/files/wiXDuiQ29xuHoPxwxGFw</a></td><td></td></tr></tbody></table>

### Need help? Get in touch!

If you have a customer success manager, don't hesitate to contact them. We are always happy to jump on call.

Additionally, you can:

* reach us via the live chat in the editor - watch out for this icon in the sidebar: <img src="/files/yXGKw63xns9jdVS2p561" alt="" data-size="line">
* send us an email to <support@uify.io>


# Quickstart

{% embed url="<https://www.loom.com/embed/b4426467b97e4f3c9cccb04bf7f0758c>" %}


# Visual UI Builder

Uify comes with an extensive library of customizable UI components. User interfaces are created by simply dragging components from the library to the grid-based application canvas.&#x20;

![](/files/VEaEUy00yt1Buw53m9gy)

Components can be resized, duplicated (CMD+D or OPTION+drag for Mac, CTRL+D or ALT+drag for Win), copy-pasted (CMD+C/V for Mac, CTRL+C/V for Win), and deleted (Backspace). Besides just using drag-and-drop to reposition components, you may also use the arrow-keys to move components in any direction, as long as no other component is in the way.

![Resizing and duplicating components](/files/Ds4P9pl6lEvvl2iqftbt)

In case you want to select components within another parent component (e.g. container, form, etc.), hold CMD (Mac) / CTRL (Win) while drawing the mouse selection.

![](/files/ZuW2ApFcCFfQor4W4xbi)


# Component Properties

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. For a deep dive on using Javascript in component properties, have a look over [here](/writing-code/component-properties).

A detailed description of components and their properties can be found in the [component reference](/component-reference/shared-properties) section.

![Component properties are configured in the right-hand panel](/files/yZ9SHcVA9QyHBCg1bB7p)

To customize the appearance of components, switch over to the style tab in the property panel.

![](/files/M6OLwolKUAV3zGFujyN1)


# Environments & Deployment

Newly created apps, as well as any changes to existing apps, are saved only on the default staging environment. For changes to go live for end-users in the workspace, they have to be deployed to the production environment (clicking on "Deploy" and "Deploy version" in the top right corner in the editor). Each deployment has a version number that depends on the previous version number and the type of deployment (Major, Minor or Patch). Additionally, notes can be added to each deployment.

To inspect your version history, including the version number, the date of deployment, the name of the user that deployed it and the version notes, is available when clicking on "Deploy" and "Version history" in the right top corner.


# Component properties

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.

### Property types

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](/writing-code/actions) 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](#embedding-javascript-in-text). 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](#reference) below.

### Transforming properties to JavaScript

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:

<div align="center"><img src="/files/FC7zpzHpXrneAhiNfv0x" alt="Transformation icons for boolean properties"></div>

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.

{% hint style="info" %}
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](#evaluation-preview) below)
{% endhint %}

A valid example of transforming a `boolean` property to a dynamic expression may look like this:

<div align="center"><img src="/files/qYPYkl8trTjk0e7VsPJ3" alt="Using a JavaScript expression to set the boolean Required property"></div>

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](#reference) to find out which property types can be transformed to JavaScript expressions, and which data types they expect.

### JavaScript properties

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:

<figure><img src="/files/HNgW9yF35Tf3KvM8vZ5J" alt=""><figcaption></figcaption></figure>

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:

<figure><img src="/files/IohSbr4HRmpLIbkxzKXv" alt=""><figcaption></figcaption></figure>

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:

```typescript
// Single quotes work
'Hello'

// Double quotes too
"Hello"

// And template strings as well
`Hello`
```

### Embedding JavaScript in text

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](https://mustache.github.io/)-like syntax. You can enter regular text, but embed expressions by wrapping them in doubly-curly braces as follows:

<figure><img src="/files/DSuNJSiFX4vwQHcedHcc" alt=""><figcaption></figcaption></figure>

You can embed as many expressions as you want, and they will all be replaced with the evaluated value at runtime.

{% hint style="info" %}
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:

<img src="/files/6QcOw8KwX5Lz3A9XcwaE" alt="" data-size="original">\
\
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.
{% endhint %}

### Evaluation preview

We have learned so far about two ways to dynamically calculate property values:

* [JS-properties](#pure-javascript-properties), that expect a JavaScript expression
* [Text-properties](#embedding-javascript-in-text), 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:

<figure><img src="/files/zihvQubCSRgHLgqmjPkU" alt=""><figcaption></figcaption></figure>

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:

<figure><img src="/files/xLukfDQM3hAhHx2gJFNU" alt=""><figcaption></figcaption></figure>

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:

<figure><img src="/files/p7F9T8mCASm4iecbQZQo" alt=""><figcaption></figcaption></figure>

Similar to the recommendation that [embedded expressions in text-inputs](#embedding-javascript-in-text) should evaluate to a string value, we recommend to follow these type recommendations precisely, and apply explicit conversions where necessary.

### Reactivity

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](/writing-code/exposed-variables)). 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:

![Displaying the value of the input field in a text-component](/files/D1J6NTtU9gXGCTlOmsyw)

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:

![Cascaded re-evaluation of several properties on input value change](/files/ZRYLNc8f0GpaEaOTq9Yx)

Let's unfold this example step by step:

1. `productName` is a regular text-input. Its value should be mirrored in the other two input fields.
2. `nameCopy1` receives the dynamic expression `{{productName.value}}`. This registers a dependency between its value and the `value` property of `productName`
3. `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":

1. All properties depending on `productName.value` are looked up from the internally managed dependency graph. This resolved to the `value` property of `nameCopy1`.
2. The property configuration of that `value` property, i.e. `{{productName.value}}`, is re-evaluated by the runtime, and evaluates to "Macbook".
3. 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`.
4. The property configuration of that `value` property is `nameCopy1.value`, which will be re-evaluated as well. This evaluation leads to "Macbook" as well.
5. 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.

{% hint style="warning" %}
There are only two possible triggers for a dynamic property configuration to be re-evaluated:

1. The configuration, i.e. the code, is changed in the properties panel or by another collaborator
2. 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.
{% endhint %}

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:

![Dependency cycles are detected](/files/LZ3Wv1jZx3ZHimlffic0)

The outcome for the affected expressions / properties is not deterministic. It is highly recommended to immediately resolve any dependency cycle that occurs.

### Expanded code editor

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:

<figure><img src="/files/FCiVZhqVe5B1W0iogfTe" alt=""><figcaption></figcaption></figure>

The expanded editor allows you to:

* Write larger expressions comfortably
* Inspect complex previews

### Reference

The following table lists all component property types and their main characteristics:

<table><thead><tr><th width="154.9442896935933">Property type</th><th width="212">Standard UI input</th><th data-type="checkbox">Transformable to JS</th><th width="150" data-type="checkbox">Embedded JS</th><th>Example property</th></tr></thead><tbody><tr><td>js</td><td>code input</td><td>false</td><td>false</td><td><code>Data</code> of a Table</td></tr><tr><td>stringWithJs</td><td>text input</td><td>false</td><td>true</td><td><code>Label</code> of a TextInput</td></tr><tr><td>boolean</td><td>switch</td><td>true</td><td>false</td><td><code>Disabled</code> of a Button</td></tr><tr><td>string</td><td>case by case</td><td>true</td><td>false</td><td><code>Label alignment</code> of a TextInput</td></tr><tr><td>array</td><td>code input</td><td>false</td><td>false</td><td><code>Options</code> of a Select</td></tr><tr><td>color</td><td>text input / color picker</td><td>true</td><td>false</td><td><code>Value color</code> in a TextInput</td></tr><tr><td>action</td><td>Dropdown select</td><td>false</td><td>false</td><td><code>On click run</code> action of Button</td></tr></tbody></table>


# Actions

Actions are the key ingredient for adding logic to your applications, and for managing interactions with your datasources. Almost like native JavaScript functions, they are small units of code that perform a specific task. They are particularly useful for:

* Reading and mutating UI component state or app state variables
* Executing interactions with datasources, like fetching data from a database or API, or writing a file to an AWS S3 bucket
* Triggering other actions
* Sending notifications

They could be triggered by UI events which they are associated with, run immediately when the app is loaded, or run periodically at a configured interval. This section covers all you need to know about actions:

1. [Managing actions](/writing-code/actions/managing-actions): How to create, organize and configure actions
2. [Execution model](/writing-code/actions/execution-model): The internals around how actions are executed and the data they expose after successful or unsuccessful execution
3. [Preview](/writing-code/actions/preview): How to test actions without influencing application state
4. [Triggers](/writing-code/actions/triggers): Full overview of all events that may trigger actions to be executed


# Managing actions

### Action editor

Actions are managed in the action editor in the bottom panel of the app editor. You can easily toggle this panel on and off by using the middle icon in the header of the editor. In case you need more space, you can expand the bottom panel by using the expand-icon in the top-right of that panel. Resizing the panel is possible by dragging the top-border of the panel up and down.

The following cast summarizes all basic UI interactions of the action editor panel:

![Managing the action editor panel](/files/ID0n5eUF0ieR8owjRbiy)

The action editor panel can be in one of three possible states:

* **Hidden**: The panel is not visible and does not distract you from building the UI and configuring components.
* **Reduced**: The panel is visible, but only takes 20-30% of the screen. This is the default state when it is activated, and works perfectly for modifications of small scripts
* **Expanded**: The panel was expanded to increase the size of the code editor. Ideal for creating or editing larger pieces of code

### Action types

Each action has a specific type, which determines how they are configured, with which datasources they may be connected, and of course how they are executed. This action type is usually tightly coupled with the datasource type that can be associated with the particular action. For example, a `MYSQL` action is able to operate on any `MYSQL` datasource of your workspace, however it may not operate on e.g. a `POSTGRES` database. A `SALESFORCE` action may perform REST requests or execute SOQL queries against a `SALESFORCE` datasource, but not with any other datasource type. You will find a dedicated action type for each existing datasource type.

On top of that, there are `JS` actions, which are not connected to any datasource at all. Instead, they allow to write custom JavaScript code, which uses state variables, mutates component state by using their exposed setters or trigger other actions ([learn more](/writing-code/exposed-variables)).

### Creating actions

Actions are created via the plus-icon above the action list. Clicking that icon opens the action type choice, in which you can select the datasource type you would like to interact with, or alternatively select the `JS` type to write native JavaScript code. The new action will be placed at the bottom of the list, which means that you have to drag and drop it to its final place manually.

![Creating a MySQL action and moving it into a folder](/files/n6NX6kLrrKiFBSY6XV7O)

{% hint style="info" %}
Actions are currently always create at top-level, i.e. it is not possible to create new actions in a specific target folder immediately. We are working on this issue and hope that we can make this experience more convenient soon.
{% endhint %}

{% hint style="danger" %}
After creating an action, its type cannot be changed. In case you have chosen the wrong type, the only way to fix it is to delete the action with the wrong type, and create a new action with the correct one.
{% endhint %}

### Organizing actions with folders

With a growing number of actions in your app, a flat action list becomes unusable fairly quickly. For that reason, the action editor allows to create a nested folder tree, similar to what you are already used to in your file system. Folders are created in exactly the same way like actions, and can be repositioned with drag and drop as well. It is easy to place actions or folders in a parent folder by dragging and dropping it onto that folder:

![Placing an action into a folder](/files/IoKGtwyL9zFqb1FqnvXr)

Dropping an item onto a closed folder will automatically expand it:

![Dropping an action onto a closed folder](/files/WVAaqkXH9C1j0DnzbV5P)

There is no limitation with regards to how many nested levels your folder tree can have, so you can create complex structures that are still maintainable and convenient to use.

### Editing actions

The [action type](#action-types) determines how exactly an action should be configured. For an SQL-based action, the main input is the code editor to write the SQL query. However, for a REST request, you will rather configure the URL path, request method, request headers, body, etc.

Therefore, the UI will look differently depending on the type of the action you are editing. Please refer to the [integration reference](broken://pages/HM6THoiQw6sBXJcVDhcU) to learn more about the configuration options for each individual action type.

The common principle for all actions, except JavaScript actions, is that you are required to select the datasource on which you would like to run the particular action. The list will only suggest datasources that have the same type like your action. For convenience, you can also create a new datasource in case that will be necessary.

{% hint style="info" %}
Like state variables and computed values, actions follow a different process of saving changes. While any changes in the application canvas and in the component properties is saved instantly, actions require you to hit the "Save"-button. This persists your changes, distributes them to other collaborators, and activates the new version of your action for future runs. The reason for this mechanism is to prevent unfinished changes disrupting the functionality of your app for yourself or other collaborators.
{% endhint %}

{% hint style="info" %}
Use the keyboard shortcut CMD+s (Mac) / CTRL+s (Win) to save changes without using the "Save"-button.
{% endhint %}

### Deleting actions

Actions can be deleted via the context menu of the action in the list. In case the action is used anywhere in a code snippet, you will receive a warning about these usages. Ignoring this warning will break these code snippets and it is therefore highly recommended to verify these usages before proceeding with the action deletion ([learn more](/writing-code/exposed-variables#deleting-entities)).

{% hint style="warning" %}
Deleting an action cannot be undone. Once an action is deleted, there is no way to bring it back. Therefore, caution is advised!
{% endhint %}


# Triggers

This section introduces and describes all possible ways of how an action execution can be triggered.&#x20;

### Manual trigger

The easiest way to trigger an action run is by executing it manually in the action editor. This is of course only possible in edit-mode and not when using a deployed app in the workspace. You can run an action manually by hitting the "Run"-button in the top-right corner of the action editor in the bottom panel. The result of the execution will be displayed in the :

![Triggering an action manually in the action editor](/files/Tf4iLVBgljvPBcmDK89W)

{% hint style="info" %}
Running an action manually is only possible for actions that have no unsaved changes. Until changes are saved, you will only see the "Save"-button in the top-right. After saving the action changes, the "Run"-button appears and allows to trigger the updated action.
{% endhint %}

### Programmatic trigger

All actions are available as variables in JavaScript actions ([learn more](/writing-code/exposed-variables#actions)) and expose a `trigger` function, which allows to run the particular action. This function returns a `Promise` that resolves to the return value of the action, or is rejected with a potential error that occurred.

All JavaScript actions are `async` by default, which means that you can use `await` anywhere, including when triggering other actions. With that in mind, an example of triggering an action programmatically might look as follows:

```typescript
// Assuming that "fetchProducts" is a REST action fetching products from an API.
const products = await fetchProducts();
productTable.setData(products);
```

Of course, you may also trigger other JavaScript actions, and the resulting Promise will resolve to the return value of that action.

Since the `trigger` function returns a `Promise`, you may trigger multiple actions at the same time, by for example using `Promise.all`:

```typescript
const results = await Promise.all([
    fetchProducts(),
    fetchComments(),
    fetchQuantities()
]);
```

### Event properties

Components are able to react to UI events, like clicking a button or focusing a text input field. These events can trigger actions, which is configured by assigning these actions to event handler properties. Usually, this is done by selecting an action in the dropdown for the particular event property:

![Assigning an action to the click-event of a button](/files/QwClQfYlRNCo6X3KgdbD)

The associated action will be triggered every time the particular UI event occurs.

{% hint style="info" %}
When an action has been selected for an event property, you will see a "crosshair" icon next to the property label. Clicking on that icon will conveniently open the action editor in the bottom panel and focus the selected action.
{% endhint %}

### Scheduled execution

Actions can be configured to run automatically based on a given schedule. You can access these trigger settings by clicking on the "Trigger" tab in the action configuration:

![Accessing the trigger configuration for an action](/files/ov7v4qWsPG6uhaVE9enE)

The following settings can be managed there:

* **Run action at page load**: Setting this trigger will execute the action when the app is loaded. It is therefore similar to running JavaScript code on page load in traditional web engineering.
* **Run action periodically**: With this trigger, an action can be configured to run in a specific interval, provided in milliseconds. Please note that this setting automatically **includes** running the action at page load, i.e. the action will run at page load, and then every time after the given amount of milliseconds. In this regard, the behavior differs from the native behavior of `setInterval` in JavaScript.
* **Minimum action delay**: With this setting, you can configure the minimum amount of time that must have passed between two executions of an action. Executions that are triggered earlier than that are not cancelled, but postponed in a queue until the given amount of time has passed. See this example in which three fast clicks lead to delayed execution of an action:

![Action executions with before minimum delay are postponed for later](/files/Zf6B2EP1kvWquu5lZG1V)

Please familiarize yourself with the following potential pitfalls and details to be aware of:

{% hint style="warning" %}
In case both periodic execution as well as minimum delay are configured, you need to make sure that the periodic interval in milliseconds is not less than the minimum delay. Otherwise, more and more action executions will queue up and ultimately lead to failure.
{% endhint %}

{% hint style="warning" %}
A too small periodic interval can lead to a similar effect of an increased queue size, e.g. in case the interval is smaller than the average execution time of an action. Make sure you set sensible interval timings to prevent that.
{% endhint %}

{% hint style="info" %}
An action is never executed more than once in parallel. Please refer to [this documentation](/writing-code/actions/execution-model#parallelization-and-timeouts) to learn more.
{% endhint %}


# Execution model

### Execution process

Every action, regardless of its type, goes through the same execution process:

1. **Constraint check:** Check whether another execution of the same action is already running, or whether a potential minimum delay has not passed yet. If yes, the execution is added to the queue for later execution. Please refer to the [documentation below](#parallelization-and-timeouts) to learn more.
2. **JS evaluation:** Once the constraint check has been passed, JavaScript code inside the action configuration is evaluated. For example, this might be embedded snippets in a SQL query, or evaluating the JSON-body of a REST request. In case the evaluation encounters an error anywhere, the action execution is aborted and marked as unsuccessful.
3. **Action run:** The action is now executed as configured, using the JS evaluation results from the second step. The execution highly depends on the action type and, except for JavaScript actions, requires a round-trip to backend. In case the backend responds with an error, or in case the action [times out](#parallelization-and-timeouts), the execution is aborted and marked as unsuccessful.
4. **JS transformation:** Actions might have JS transformation scripts configured. In this case, this code is executed, providing the raw result as input. In case this evaluation leads to an error, the raw action result is still kept as successful execution "before transformation", but the action result "after transformation" is marked as unsuccessful.
5. **Dependency reevaluation:** Following the [reactive principles](/writing-code/component-properties#reactivity) of the engine, there might be usages of the executed action that depend on specific properties, like for example `data`, `dataBeforeTransform`, `success`, or `executionTime`. After successful or unsuccessful execution of the action, these usages are reevaluated - which can trigger cascaded dependency reevaluation.

The following sections describe some of these steps in more detail.

### Parallelization and timeouts

The same action will never run more than once in parallel, regardless of how it has been triggered. In case an action is triggered while another execution of the same action is still running, this newly triggered execution will wait in the queue until other executions of that action have been finished.

The following example illustrates this mechanism very well:

![Newly triggered executions wait until others have finished](/files/Bdwiim9Wmbk9F0SRfLBz)

This example app performs the following operations:

* The click-event handler of the "Run" button increments the state variable that fuels the number on the left. You see that it increases immediately every time the button is clicked.
* Then, the `longRunningAction` is triggered, which waits for 2 seconds and then increments a state variable that is shown on the right.
* As you can see, the right-hand number is incremented with exactly 2 seconds delay, because the four executions, which are triggered by quickly clicking the button at the beginning, are strictly executed in sequence.

{% hint style="info" %}
In this example, it is important that the action that handles the button click does not `await` the result of `longRunningAction`. If we used `await` here, the left-hand figure would also increment with 2 seconds delay, because the `handleButtonClick` action will always wait for the `longRunningAction` to finish. Executions of `handleButtonClick` would then queue up in a similar way like the executions of `longRunningAction` do in the example.
{% endhint %}

With regards to **timeouts**, actions are currently not restricted in terms of how long they execute. The application will always wait until an action finishes, no matter how long this will take. Due to the sequencing of executions, that means that very long-running actions will block any further executions. It is therefore recommended to keep actions small and atomic, and of course to prevent infinite loops.

{% hint style="info" %}
The only exception of not having a timeout is the execution of a Salesforce bulk job. This action type is timed out after 120 seconds. We are working on a more consistent timeout behavior, and making it configurable for individual actions.
{% endhint %}

### Result transformation

In many use cases, data that is pulled in from external datasources must be transformed in some way. To accomplish this, any action can be configured to execute JavaScript code to transform the original result of the action. For example, a `MySQL` action returns a set of records, and a JavaScript snippets adds aggregations and data cleaning on top.

{% hint style="info" %}
JavaScript actions do not allow transformation, because the transformation logic can simply be added to the action itself.
{% endhint %}

A transformation script can be added to an action by navigating to the "Transformation" tab of the action configuration, and activating the toggle there:

![Accessing the JavaScript transformation for a MySQL action](/files/afbERC1MgLimlqafGmd2)

When this toggle is activated, a code input appears to write the transformation code. The original result of the action can be accessed via the `data` variable. The value that is returned by this transformation code will be placed in the `data` property of the action.

You will still be able to access the original result of the action via the `dataBeforeTransform` property. In summary:

* Use the `data` property of an action to access the result after transformation.
* Use the `dataBeforeTransform` property to access the original result before transformation.
* In case no transformation is configured, `data` and `dataBeforeTransform` will reference to the same result.

In case there will be errors in either the original action, or the data transformation, the action properties will be set as follows:

* `success` will only be `true` if both the original action and the JavaScript transformation ran without errors, and `false` otherwise
* `error` will contain the message of the error that occurred either in the original action or the JavaScript transformation. In case both ran successfully, it will be `null`
* `dataBeforeTransform` will contain the result of the original action, even if there was an error in the JavaScript transformation (and hence, `success` being `false`). In case there is an error in that original action run, it will be `null`
* `data` will contain the result after applying the JavaScript transformation. In case there was an error in either the original action, or the transformation, it will be `null`

### Action arguments

#### Passing arguments to action runs

Actions can receive arguments for an execution, similar to how regular functions can receive arguments. You can pass arguments of any type to an action run by providing an object as first argument to the `trigger` function with `args` as key. Formally, the signature of the `trigger` function is as follows:

```typescript
interface Action {
    ...
    trigger: (arguments?: { args: unknown }) => Promise<unknown>;
}
```

You can access these arguments by using the `args` variable inside of an action:

```typescript
// action1: Calling action2 with arguments
action2(5);

// action2: Using the provided arguments
console.log(args); // This will log the value 5
```

{% hint style="info" %}
args will always be undefined when an action is previewed. In case you want preview to work properly for an action, you need to use sensible defaults in your code for that case. This certainly is unnecessary and undesired code, and we are working on a feature to deliberately provide explicit arguments in case an action is previewed that is depending on args.
{% endhint %}

#### Default arguments

One possible [trigger](/writing-code/actions/triggers) of an action is a UI event, like the click of a button or the change of a toggle. To react to such events, an action must be associated with an event-property of a component in the properties panel. Some of these events will pass specific `args` to such action runs by default:

* Custom actions of input validation rules will receive the input value of the validated component
* An action associated with a table row action will receive the data array of the clicked row
* Actions associated with the "On rows update" and "On row add" events of a table will receive an object summarizing the changes

We will continue to add more default arguments like that to specific events, to make it easier for engineers to consider the context of the particular event.

### Dependency reevaluation

JavaScript snippets in dynamic component properties can not only use properties of other components, but also [properties of actions](/writing-code/exposed-variables#actions). Due to the [reactive approach](/writing-code/component-properties#reactivity) of our engine, such snippets should also be reevaluated as soon as a utilized property of an action changes. An action run will therefore trigger such updates as soon as it has finished, regardless of whether it was successful or not. You can therefore rely on your snippets always being updated with the latest action property values:

![Component properties react to action changes](/files/CwW6sTTHYIfIa7UbsMMo)


# Preview

This section is still work in progress.


# State variables

Application state is a fundamental concept in software engineering. "State variables" and "computed values" are useful features to encapsulate state in a single place, and to prevent duplication of code. Despite the common property of holding state values, there is a structural difference between these two entities:

* **State variables** are initialized with an explicit value and can thereafter be explicitly mutated via a setter-function in JS actions. You can imagine them behaving similar to React state or redux. They are perfect for maintaining atomic pieces of state that are managed explicitly in user code.
* **Computed values** are "named calculations" based on a JavaScript expression. They will reevaluate automatically in case any of their dependencies change (e.g. component properties, action results, or other state variables or computed values). They are perfect for encapsulating repetitive calculations in a single place with a guarantee that their values are always up to date.

As simplification, we are going to use the term "variable(s)" in this section to address both state variables and computed values.

{% hint style="warning" %}
State variables are supposed to hold serializable values. Use primitives, objects and arrays instead of maps, sets, dates, etc.
{% endhint %}

### Managing state variables

Both state variables and computed values are managed in the `Explorer` tab of the left-hand panel of the app editor:

![Management panels for state variables and computed values](/files/9iRkkMvPtuXWtaiKxJg0)

#### Inspecting variables

Each variable is listed with its name and the type of the value it currently holds. Clicking on a variable will expand the view to show details, like its current value. For complex values like arrays and objects, an inspector-view is rendered, which allows to selectively drill down into the object:

![](/files/K9doPrDwb2x5dba1VEvd)

#### Creating variables

Clicking on the plus-icon next to the header of the variable list leads to the variable creation form. It requires you to provide a name as well as a JavaScript expression to define the initial value (state variable) or value computation (computed value) for the newly defined variable. Variable names must be unique across all state variables and computed values, however may overlap with components or actions. Use the "Save"-button to create the new variable and return to the list view.

{% hint style="info" %}
Use the keyboard shortcut CMD+s (Mac) / CTRL+s (Win) to submit this form, either for creating or for editing variables.
{% endhint %}

#### Editing variables

The context menu of a variable can be accessed by clicking on the dotted icon that appears on hover next to the variable name. The "Edit" menu item leads to the same form that is used for creating a new variable. Both the name and the JavaScript expression for initial value / value computation can be modified. Modifying variable names will automatically update all usages of that variable in other code snippets ([learn more](/writing-code/exposed-variables#renaming-entities)).

![](/files/bUK7jDU9VBQsTsK7jSM7)

#### Deleting variables

Deleting a variable is possible through the same context menu like for entering edit-mode. To prevent accidental deletion of a variable that has usages in other code snippets, you will be warned when you try to delete such a variable.

{% hint style="danger" %}
Deleting a state variable or computed value cannot be undone. Deleting a variable with usages, despite the warning, will break all code snippets in which the deleted variable has been used ([learn more](/writing-code/exposed-variables#deleting-entities)).
{% endhint %}

### Mutating state

State variables require to explicitly change their value in JS actions. For that, the `state` variable is exposed in any place the user may use JavaScript ([learn more](/writing-code/exposed-variables#state-variables)). This `state` object offers a getter property with the same name like the variable, as well as a setter function named by the pattern `set{VarName}`, for example `setCouponData` for a state variable called `couponData`. The [reactive behavior](/writing-code/component-properties#reactivity) of code snippets in component properties covers state variables and computed values as well, i.e. property values will update automatically as soon as a variable value changes. The following example of a simple counter highlights these principles:

![A simple counter, using a numeric state variable](/files/ZF5dgzvCIQCUg694QRLx)

### Managing computed values

Computed values are created, edited and deleted in the same way like state variables. There are a couple of important differences though:

* Computed values do not have an "Initial value" like state variables, but rather expect a JavaScript expression that is watched for changes.
* The `state` object does not offer a setter function, because the value will update automatically as soon as a dependency of its calculation expression changes

When defining the calculation expression, you have access to the same [exposed variables](/writing-code/exposed-variables) that are also available in component properties or JS action code. Similar to component properties, you cannot access functions though, because the evaluation of this expression is not deterministic ([learn more](/writing-code/exposed-variables#exposed-functions-in-component-properties)). Sticking with the example of a counter, we may use the `counter` variable now in a computed value to derive new values from it:

![Computing a derived value from the counter variable](/files/4GQME9ERWi5Q0nrYYGdX)

### Utilizing state values

State variables and computed values are exposed in JavaScript expressions through the `state` object. This object provides all variable values as getter properties with the same name like the associated variable, and a setter function for each state variable.

In the previous example of a simple counter, we have been using two variables:

* `counter`: state variable holding the current numeric value of the counter, being incremented or decremented explicitly by clicking the buttons
* `counterSquared`: computed value, which calculates the squared value of the counter, by using the JavaScript expression `state.counter * state.counter`.

The `state` object will look as follows in this setup:

```typescript
interface State {
    counter: unknown;
    counterSquared: unknown;
    setCounter: (value: unknown) => void;
}
```

{% hint style="info" %}
Our type system is not yet capable of inferring a better type than `unknown` for the getters and setters. It is highly recommended to keep a consistent type in these variables to keep your application robust. We are working on better inference and hope this will be available soon!
{% endhint %}

JavaScript code in component properties and in the calculation expressions of other computed values will update automatically as soon as a state variable or computed value is updated ([learn more](/writing-code/component-properties#reactivity)).

{% hint style="danger" %}
Like with component properties, it is easy to create **dependency cycles** with computed values. The simplest scenario is two computed values that just reference each other in their calculation expressions. You will be notified with a warning in such case and it is highly recommended to resolve these dependency cycles immediately!
{% endhint %}


# Exposed variables

In the previous sections, you learned how to create highly dynamic UI behavior by using JavaScript expressions in the component property configurations. You also learned how to write actions, and how to define state variables to encapsulate computations and maintain application state in a single place. Such pieces of code are however only as useful as the variables, functions and libraries that you have available. In this section, you will learn more about the exposed variables and objects that you have at your disposal in any place you can use JavaScript in.

### UI components

Every component, that exists anywhere on the canvas, is available in JavaScript code as variable with the same name. These variables reference to objects that expose a selected list of getters and setters for their particular properties. These component objects do not expose every single property that is available in the properties panel, but only a selective list that are considered most useful.

For example, a TextInput component exposes a getter for the value in the input called `value`, and a setter called `setValue` to change the input value.

```typescript
// Using the input value
const inputValue = myInput.value;
console.log(`Input value: ${inputValue}`);

// Changing the input value
myInput.setValue('');
```

Please check out the [component reference](broken://pages/8UvcMZT1x3xrGW6YYPbw) to see a full list of all exposed properties and functions of each component type.

### Actions

Equivalent to components, every action of an app is exposed as variable with the same name in any JavaScript snippet. These action objects allow to trigger the underlying action and expose properties of its most recent execution:

```typescript
interface Action {
    // Internal action type name, for example 'JS'
    readonly type: string;
    // Name of the action, as managed by the user in the action editor
    readonly name: string;
    // Whether the most recent execution was successful
    readonly success?: boolean;
    // Error message of the most recent execution in case it was not successful.
    // null otherwise.
    readonly error?: string | null;
    // Execution time in milliseconds of the most recent execution,
    // regardless of success
    readonly executionTime?: number;
    // Returned data of the most recent execution, after applying js-transformations
    readonly data?: any;
    // Like data, but before applying js-transformations
    // Does not apply to JS actions
    readonly dataBeforeTransform?: any;
    // Runs the action and returns a promise that resolves to its return value,
    // or is rejected in case the action run fails due to an error
    readonly trigger(value?: { args: any }): Promise<any>;
}
```

{% hint style="info" %}
`success`, `error`, `executionTime`, `data` and `dataBeforeTransform` will be `undefined` in case the particular action has not been executed yet.
{% endhint %}

Please refer to the [actions section](#actions) to learn more about actions, their execution and how to pass arguments into an action run.

### State variables

All state variables and computed values are exposed in code via the `state` object. This object contains a property for each state variable and computed value, with the same name as those entities are defined. These properties will provide the current value of the particular variable. On top of that, it offers a setter for for each state variable to mutate its value. These setters are named by pattern `state.set{variable-name}`, e.g. `setProductName` for a state variable called `productName`:

![All state variables and computed values are accessible through the 'state' variable](/files/7hZfnfmN5AfWZkVC4CkK)

Computed values do not need a setter, because their value should always be the outcome of the provided JavaScript snippet. Please refer to the [state variables section](#state-variables) to learn more about state variables and computed values.

### Renaming entities

Since every component, action and state variable is exposed as variable in JavaScript snippets by its name, you might wonder whether renaming such entities will break your logic. Fortunately, the app editor takes care of such renaming operations automatically and updates all usages as well. You can see this demonstrated in the following example:

![Renaming a component automatically updates all usages as well](/files/jmn7pt55FOWh6wp2QJYI)

You can rename any entity with the confidence that no JavaScript snippet in properties, actions or state variables will break due to that operation.

### Deleting entities

The user may delete a component, action or state variable at any moment. That deleted entity may however be used in code snippets anywhere across the application. Unfortunately, deleting such entity anyway will break all usages of the related variable in JavaScript code snippets. The user is notified about existing usages to prevent accidentally breaking the application, this might however be ignored and she might proceed with deleting anyway.

In that case, existing usages will be replaced with a question mark (`?`), which will lead to runtime errors when these code snippets will be evaluated:

![Deleting a component with usages leads to erroneous code snippets](/files/Q8JHdpbBTp65dmCShoPi)

In case entities with usages are deleted despite the provided protections, there is no alternative to checking all snippets in the application for broken code. Fortunately, undoing the harmful deletion will fix all usages automatically.

### Authenticated user

The currently authenticated user is exposed as a variable called `currentUser`. It provides basic details about the user:

```typescript
interface CurrentUser {
    readonly id: string;
    readonly email: string;
    readonly organization: string;
    readonly organizationId: number;
    readonly firstName: string;
    readonly lastName: string;
    readonly initials: string;
    readonly photoName?: string;
    readonly role: 'admin' | 'editor' | 'viewer';
    readonly createdAt: string;
    readonly updatedAt: string;
    readonly active: boolean;
}
```

You can use this object like any other variable in your JavaScript snippets:

![Using the currentUser variable](/files/NlX0Q5B9aiJfkYRBm33l)

### Libraries

The open-source community provides many useful libraries to solve common problems in software engineering. While developing applications in Uify, you may benefit from the following libraries by default:

<details>

<summary><a href="https://www.npmjs.com/package/nanoid">nanoid</a>: Create unique IDs with the default behavior or a custom alphabet and length</summary>

```typescript
// Default ID with 21 characters
const id = nanoid();
// ID with a custom length of 12
const id = nanoid(12);
// ID with a custom alphabet
const id = nanoid.customAlphabet('1234567890abcdef', 10);
```

</details>

<details>

<summary><a href="https://www.npmjs.com/package/date-fns">date-fns</a>: Selected functions for dealing with dates</summary>

```typescript
interface DateFns {
    isPast(date: Date | number): boolean;
    isFuture(date: Date | number): boolean;
    isAfter(date: Date | number, dateToCompare: Date | number): boolean;
    isBefore(date: Date | number, dateToCompare: Date | number): boolean;
    isDate(value: any): boolean;
    isValid(date: any): boolean;
    endOfWeek(date: Date | number, options: { weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 }): Date;
    format(
        date: Date | number,
        format: string,
        options?: {
            weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
            firstWeekContainsDate?: number;
            useAdditionalWeekYearTokens?: boolean;
            useAdditionalDayOfYearTokens?: boolean;
        },
    ): string;
    add(date: Date | number, duration: Duration): Date;
    addBusinessDays(date: Date | number, amount: number): Date;
    addDays(date: Date | number, amount: number): Date;
    addHours(date: Date | number, amount: number): Date;
    addISOWeekYears(date: Date | number, amount: number): Date;
    addMilliseconds(date: Date | number, amount: number): Date;
    addMinutes(date: Date | number, amount: number): Date;
    addMonths(date: Date | number, amount: number): Date;
    addQuarters(date: Date | number, amount: number): Date;
    addSeconds(date: Date | number, amount: number): Date;
    addWeeks(date: Date | number, amount: number): Date;
    addYears(date: Date | number, amount: number): Date;
    differenceInMilliseconds(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInSeconds(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInMinutes(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInHours(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInDays(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInCalendarWeeks(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInWeeks(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInCalendarISOWeeks(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInCalendarMonths(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInMonths(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInCalendarQuarters(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInQuarters(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInCalendarYears(dateLeft: Date | number, dateRight: Date | number): number;
    differenceInYears(dateLeft: Date | number, dateRight: Date | number): number;
    parse(
        dateString: string,
        format: string,
        referenceDate: Date | number,
        options?: {
            weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
            firstWeekContainsDate?: number;
            useAdditionalWeekYearTokens?: boolean;
            useAdditionalDayOfYearTokens?: boolean;
        },
    );
    parseISO(
        argument: string,
        options?: {
            additionalDigits?: 0 | 1 | 2;
        },
    ): Date;
    startOfWeek(date: Date | number, options: { weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 }): Date;
    sub(date: Date | number, duration: Duration): Date;
    subBusinessDays(date: Date | number, amount: number): Date;
    subDays(date: Date | number, amount: number): Date;
    subHours(date: Date | number, amount: number): Date;
    subISOWeekYears(date: Date | number, amount: number): Date;
    subMilliseconds(date: Date | number, amount: number): Date;
    subMinutes(date: Date | number, amount: number): Date;
    subMonths(date: Date | number, amount: number): Date;
    subQuarters(date: Date | number, amount: number): Date;
    subSeconds(date: Date | number, amount: number): Date;
    subWeeks(date: Date | number, amount: number): Date;
    subYears(date: Date | number, amount: number): Date;
};
```

All these functions can be accessed via the `dateFns` variable in any code snippet:

```typescript
const inputDate = dateFns.parseISO(myInput.value);
const threeDaysLater = dateFns.addDays(inputDate, 3);
```

</details>

{% hint style="info" %}
We are aware that this restricted set of default libraries is not at all sufficient to address all possible use cases and that there are other essential libraries out there (e.g. lodash, validator, papaparse, ...). The ability to explicitly load and use any npm package in any app is on the feature roadmap and will hopefully be released in the near future.&#x20;
{% endhint %}

### Inbox notifications

The Inbox is an essential part of the workspace. You may send notifications programmatically to any member of your workspace to, for example, notify about events, request an approval or communicate errors. This is possible by using the `sendNotification` function of the exposed `inbox` object. It currently has a fairly basic signature:

```typescript
interface Inbox {
    readonly sendNotification: (
        recipientEmail: string,
        message: string,
        destinationAppId?: string
    ) => Promise<void>
}
```

The recipient will receive the notification to her or his inbox immediately. When clicking on this notification, the recipient will be navigated to the destination app. If no `destinationAppId` was provided, the destination app will be the same from which the notification was sent. Otherwise, the user will be navigated to the app with the provided `destinationAppId`. In case a notification was sent from edit-mode, the user will end up in edit mode as well when clicking on the notification. This makes it easier to test programmatic notifications while building a new application before releasing it to production.

Sending a notification is an asynchronous operation, which is why it returns a `Promise`. Due to that, the `sendNotification` function is usually called with `await`:

```typescript
await inbox.sendNotification(
    'jane.doe@corp.com',
    'A new client has signed up and requires your approval'
);
```

{% hint style="info" %}
Trying to send a notification to an email that is not part of your workspace, or using a `destinationAppId` that does not exist, will result in an error. In case you await the result as shown above, this means that the action will fail. Otherwise, the action will succeed without error, which usually is not what you want.&#x20;
{% endhint %}

### Console

The browser console is a useful tool for ad-hoc debugging during application development. This is possible with the object exposed as `console` variable, which allows you to log messages to the console:

![Writing log-messages to the browser console](/files/p2auPoHp1EglIf7044Uz)

### On-screen notifications

You may want to display notifications to the user to communicate successful completion of an operation or to issue a warning or error message. This is possible by using the `notify` object, which exposes different functions to dispatch notifications with different severity. The amount of time, in milliseconds, for which the particular notification will be displayed must be provided as second argument:

![Dispatching notifications of varying severity](/files/x4i3OZYAFZoUDmzRjGEX)

### Sleep

In JS actions, you can access the `sleep` function, which returns a `Promise` that will resolve in the given amount of milliseconds. Usually, this function is triggered with `await`, to delay further execution of an action. The signature of this function is as follows:

```typescript
function sleep(ms: number): Promise<void>;
```

### Exposed functions in component properties

The previous sections presented many different functions that are exposed to the user in JavaScript snippets. Some examples of such functions are:

* Setters for component properties
* Triggering an action via the `trigger` function
* Changing the value of a state variable via the `state.setVariableName` function
* Showing an error notification via the `notify.error` function

In theory, these exposed functions may be used in the code that you may place in `js` properties, or that you embedded in `stringWithJs` properties, in the [properties panel of a component](/writing-code/component-properties). Due to the reactive nature of these properties, these code snippets will be executed whenever the runtime sees a necessity for such re-evaluation, including all associated effects like running an action, changing a component property value or displaying a notification. This might be not exactly deterministic, or may change over time.

For that reason, no functions of the exposed objects are available in such code snippets of component properties. The operations which are performed in such code snippets should be limited to reading values. This implies, that exposed objects that exclusively offer functions (like `console` or `inbox`) are not available at all for code snippets in the properties panel.


# Type safety

A solid type system is an important driver of reliable and maintainable software systems. While coding in Uify is currently focused on JavaScript as primary programming language, there is a basic type system in place to avoid errors and guide the user towards correct usage of the exposed API.

The built-in type system currently supports the user in the following places:

* Getters and setters of components, e.g. the `setValue` function of a Text Input component expects `string | null` as argument, and accessing `value` returns a `string`
* All exposed properties of actions are typed (e.g. `executionTime` is `number | undefined`, `success` is `boolean | undefined`, and so on) and the `trigger` function returns `Promise<unknown>`
* Other exposed objects like `dateFns`, `nanoid`, `state`, `notify` and `inbox` have strictly typed properties and methods

You can find details about all types of exposed properties and functions in the component reference.

Violations are highlighted in our code editor in the same way such errors are displayed in your typical IDE:

<figure><img src="/files/LHaHfd6LsoUuDRyFt66p" alt=""><figcaption><p>Various type errors</p></figcaption></figure>

In this example, there are multiple violations against the type system:

* `setValue` expects a `string` or `null`, but the provided value is of type `number`
* `state.setMyVariable` expects a single argument (the next value of the state variable `myVariable`), but two arguments were provided
* The `notify.success` function expects two arguments (the message and a timeout in milliseconds), but only one argument was provided

Hovering over the place of error provides additional information about the error. Additionally, hovering over properties or functions reveal their particular type definition:

<figure><img src="/files/SslMmLPj4bkX1RoDmrKz" alt=""><figcaption><p>Type information on hover</p></figcaption></figure>

### Type system future

The current type system is only the beginning of what we envision for the future of type support in Uify. The following features could drastically improve the coverage and usefulness of the type system:

* Ability to declare types of state variables, to have explicit typing of setters and getters of such variables.
* Automatic type inference of action results. For example, the fields of database query result sets could be automatically inferred to provide a better typing of the action `data` property than just `unknown`&#x20;
* Ability to declare accepted types of action `args`, to highlight invocations of the `trigger` function with incomplete or wrong arguments, and to use `args` inside of an action in a typed manner
* Full TypeScript support in all code editors

We are going to add such features step by step, and hope that it leads to a better developer experience and more reliable software being built with Uify.


# Managing usages

The elements of an app (actions, components, state variables) can be used in many different places. For example, actions can be used in event props of components, could be triggered from within JS actions, or their properties might be accessed in any other component props or actions. State variable values could be accessed in any component property as well, or might be modified in JS actions using the exposed setter functions. Such usages could be not very obvious - they might occur in JS transformations of actions, or could appear in nested properties, like for example a table column or an input validation rule.

Keeping track of these usages becomes very difficult the more complex the app becomes, and applying changes to an app - like deleting things, or changing signatures / structures - hence could break app functionality. This problem exists in traditional engineering as well, and is beautifully solved with sophisticated IDE features for tracking usages of files, modules, objects and variables. The Uify app editor provides similarly powerful features to make usages transparent, and to prevent harmful modifications. This significantly increases the robustness of applying changes, and gives peace of mind while maintaining apps that are used in production.

### Preventing harmful deletes

Our app editor constantly keeps track of usages in various places where code can be used. The result is a sophisticated dependency graph, which allows us to make usages transparent and to dispatch warnings for potentially harmful modifications.

Whenever there is an attempt to delete a component, action or state variable that is used anywhere in code of the application, a warning is shown before such actions will be performed:

<figure><img src="/files/VYNwhFSpsO2rDmvLqVCM" alt=""><figcaption><p>Preventing the harmful delete of the text input</p></figcaption></figure>

In this example, the `value` of `textInput1` is used on the `value` property of `text3`. In case this input was deleted, we would end up with an erroneous script in this property of `text3`, which could be devastating for the correct functionality or usability of this app.

To prevent such mistakes, a warning is shown in such cases, which provides a hint for how often, and where exactly, the particular component, action or state variable is used. The user can then make use of this information and check whether the deletion should really be followed through.

It is possible to ignore this warning and to perform such deletions anyway, this however will most likely lead to broken application logic.

### View usages

Every component, state variable and action is able to display all of its usages across the entire application. This option is available in the context menus of actions and state variables, and in the menu that appears when right-clicking on a component on the canvas:

<figure><img src="/files/BeR4NCzeKiUqojUClwXL" alt=""><figcaption><p>Menu option to show usages of a component</p></figcaption></figure>

The resulting popup visualizes a hierarchical view of all usages of the particular entity:

<figure><img src="/files/cjSjAau5KvSNtZ6Plben" alt=""><figcaption><p>Displaying a single usage of the <code>textInput1</code> component</p></figcaption></figure>

The code editor on the right-hand side allows to modify these usages immediately, without the need to navigate to the original place of use manually. A larger example of such a usages overview might look like this:

<figure><img src="/files/rJ4SR53tzMQ4tQdm1PAy" alt=""><figcaption><p>Overview of multiple usages</p></figcaption></figure>

As you can see, usages within the same component, action, or state variable, or even within the same property, are grouped together for convenience. This overview makes it very easy to understand the dependencies of different entities with one glance, and even to modify them in-place.

### Going to usages

Despite the ability to modify usages immediately, you might want to rather jump to the original place where the usage occurred. This is possible by clicking on the icon in the top-right corner. The popup will close and the app editor will navigate the user to the place of usage, which can have multiple implications:

* If the usage occurs in an action, the action editor is opened and the particular action is selected. In case the usage occurs in another tab than the default, e.g. in a JS transformation, the tab is switched accordingly.
* If the usage occurs in a prop of a component, the component is selected, the properties panel on the right-hand side is opened and scrolled to the exact place of usage.
* If the usage occurs in a state variable, the explorer tab on the left-hand side is opened, and the edit-view of the particular state variable is opened.
* If the usage occurs in a nested property, like a table column or a validation rule, the nested property popup is opened accordingly.

Regardless of the place, the text selection will always highlight the exact usage, and the code editor will be scrolled to the right position so that the usage will be in view:

<figure><img src="/files/ekFOKqdOcZIgU0NF6qht" alt=""><figcaption><p>Navigating to a usage in an action</p></figcaption></figure>

### Modifying usages

Changing usages on the right-hand side of the usages popup is a very convenient option, however works slightly different depending on the type of usage.

**1. Event properties of components**

Event properties, like for example the "On click" property of a button, allow the user to select an action from a dropdown. The selected action will be executed when the related UI event occurs. This is in fact an important usage of an action, however does not involve any code. For that reason, the same dropdown is displayed in the usages popup as well:

<figure><img src="/files/N3QpxL1jdX3t8nkHeSvf" alt=""><figcaption><p>Changing an action associated with an event property</p></figcaption></figure>

Changing the selected action will immediately change the configuration and will be persisted - same behavior like when this property is changed directly in the component properties panel.

**2. Usages in action code or state variables**

Action and state variable code can be adjusted in-place. However, those have a different persistence mechanism: While changes to component properties are saved instantly, saving actions or state variables is a deliberate user action, making use of a "Save" button. To not mix approaches, the code editing in the usages popup follows the same principles.

<figure><img src="/files/dDsKB9RnvGG9YJiGxMNG" alt=""><figcaption><p>Saving changes to an action requires using the "Save" button</p></figcaption></figure>

Closing the usages popup without saving the action will not discard your changes, but you will have to find the modified action in the action editor and save changes there.

State variables do not have a "draft state", which means that unsaved changes to state variables are unfortunately lost when the usages popup is closed.

**3. Usages in coded component properties**

Code changes of component properties are persisted instantly, and therefore follow the same UX mechanics like modifications directly in the properties panel.

### Inline usages

The features described above are very useful to track usages across different entities. In addition to this, it is helpful to find variable declarations within the same action. This is easily possible by right-clicking on a variable and choosing "Go to Definition" or "Go to References":

<figure><img src="/files/izdkJDo1vILdYubGT8EO" alt=""><figcaption><p>Tracking inline usages</p></figcaption></figure>


# Console

The built-in console is part of the editor and helps to investigate and resolve issues that occur when writing and executing actions. It is located in the footer at the bottom left-corner of the editor.

## Debugging actions

The console shows logs, errors and warnings that are registered within the actions of your app. If you have been working with dev tools in your browser before, the console will feel very familiar to you.

It support the following methods:

* [`console.clear()`](https://developer.mozilla.org/en-US/docs/Web/API/console/clear)
* [`console.debug()`](https://developer.mozilla.org/en-US/docs/Web/API/console/debug)
* [`console.error()`](https://developer.mozilla.org/en-US/docs/Web/API/console/error)
* [`console.info()`](https://developer.mozilla.org/en-US/docs/Web/API/console/info)
* [`console.log()`](https://developer.mozilla.org/en-US/docs/Web/API/console/log)
* [`console.warn()`](https://developer.mozilla.org/en-US/docs/Web/API/console/warn)

For example, if you want to log the current value of a variable, you can simply `console.log` the variable and inspect its content in the console.

<figure><img src="/files/yiXoOg41wVib8xDWBe2W" alt=""><figcaption></figcaption></figure>

Additionally, the console logs errors in your code and points to the respective action and line in the code. In this example, we have a syntax error, a missing closing parenthesis. The number of errors in the console are displayed next to its launcher. At the end of the error's line, the action and line is mentioned and you are directed to it upon clicking it.

<figure><img src="/files/xvmLMZzJmDtPmPDC33Ol" alt=""><figcaption></figcaption></figure>

The console can be cleared by clicking on the broom icon.

<figure><img src="/files/13znSPidBxfTuR6sONoc" alt=""><figcaption></figcaption></figure>

Additionally, the timestamps of the errors can be toggled on and off by marking the checkbox on the top left of the console.


# Data sources

Data Management is the place your data source connections live. Here you can create and modify connections to your internal systems or third-party applications and explore their content. It gives you an overview of all the data used in your apps.

## Connecting a new data source

For each type of resource, you can find a detailed description of how you connect it in the [Integrations](/integrations/bigquery) section. The procedure is always the same. You click on "New source" and select the data source you wish to connect. After entering all required details or connecting to the service via OAuth2 you save the data source connection by clicking on "Connect".

<figure><img src="/files/1Rj4RHLzMsJgtBqyp50x" alt=""><figcaption></figcaption></figure>

## Inspecting the structure

The navigation lists all the data sources that you connected. For all databases, you will find a tree structure with multiple levels that resembles how the databases is structured internally. For example, for a PostgreSQL it will resemble it's schema. For a Google Sheet, you will find all whitelisted files on the first level and each sheet/tab in the Google Sheet file on the second level.

<div align="center"><figure><img src="/files/e9ptQ7MUoQkXcyoupxk2" alt=""><figcaption></figcaption></figure></div>

For resources that specify a connection to an endpoint, to a data storage that is not hierarchically structured or where the structure is not known in advance or subject to change, we do not provide a multi-level image of its inner workings. Examples include Slack, OpenAI and the REST API.

Since each data source can be configured for Staging or Production and the content of the data source can be entirely different, we allow you to switch between the two environments in the navigation:

<figure><img src="/files/9o1W9gaCfy5Ij9RGu0i7" alt=""><figcaption></figcaption></figure>

## Refreshing the structure

Naturally, the content of your database can change. For example, a table might be added, deleted or moved. To keep the structure of your data source up to date, you can click on the refresh icon in the top of the navigation bar. The currently selected data source will then be refreshed. A loading indicator signals if the refreshing process is still ongoing and will vanish when it is done. Changes to the structure of the database, like additions and deletion of tables, will then also be reflected in the navigation bar.&#x20;

<figure><img src="/files/KPXEpZwC7JbRbkVxbkca" alt=""><figcaption></figcaption></figure>


# Table preview

While the primary purpose of data management is the configuration of external data sources, it also allows you to explore the content of these data sources.

When you click on a table in the navigation, you will see a brief preview of your data. By default, we fetch the first 500 records. The table is paginated which will allow you to browse through your dataset. The column header is fixed and the table is scrollable vertically and horizontally. You can refresh the content of your table anytime via the refresh button in the bottom right corner.

<figure><img src="/files/tf6D8640FsQ2gzLF8UPi" alt=""><figcaption></figcaption></figure>

## Spreadsheet sources

Contrary to table-based data sources, spreadsheets allow you to set values in any cell in the sheet which can result in multiple table or table-like structures in one sheet. At the moment we only allow to display one table per sheet. We automatically infer the starting and ending cell of the table using heuristics. If your table is not correctly displayed, try putting it in a proper table format with column headers and rows without any cells that are not part of the table content above it.

## Disable preview

<figure><img src="/files/ZqCHoLCkhuuTJL7eFvVE" alt=""><figcaption></figcaption></figure>

To allow you to preview your data, we automatically fetch the schema and a preview of the table content from your data source. For users that are mindful about the number of queries or API calls that are made to their data source, we provide an option to deactivate the table preview and schema fetching in the data source configuration.


# Secrets

For sensitive connection details that you don't want to expose to other users, we allow you to specify secrets. Secrets are not exposed after their initial creation but can be used by any user when filling out the data source configuration. The secrets are stored and inserted in the backend and never touch the frontend.&#x20;

## Creating and modifying secrets

You can maintain your secrets in the workspace settings. You can find the settings by clicking on the dropdown next to the workspace name and then on "Settings". Here you will find a section **Security Settings** that includes the subsection **Secrets**.

<figure><img src="/files/l33gvQ5pZMRoxmh3sbGN" alt=""><figcaption></figcaption></figure>

You can create a new secret by clicking on "**Create new secret**". There you can specify a **name** and a **value**. After creation the only the name and the timestamps of its creation and last update are available. You can **delete** the secret via the context menu and **edit** it. When you want to update the value of your secret, the current value is not exposed, i.e. you can only add a new one. The name of the secret is immutable.

## Using your secrets

When you want to connect a data source, some configuration details can be filled in using your previously specified secrets. The secret is available in any input that has `{{` at the end of its field. Clicking on it opens up a dropdown from which the secret can be selected.

<figure><img src="/files/zF7zGCqCdb1VDvpCU8R3" alt=""><figcaption></figcaption></figure>

&#x20;Additionally, we allow you to insert the secret manually by typing `{{secrets.<name-of-my-secret>}}` where `<name-of-my-secret>` is the name of your secret.


# BigQuery

You can connect to your BigQuery data warehouse to explore data, combine them with other data sources and build apps on top of them as a data foundation.

## Connecting to your BigQuery Warehouse

To connect your BigQuery data warehouse, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **BigQuery** from the list of available data sources.
3. Name your data source connection and provide the connection details:

<table><thead><tr><th width="201">Setting</th><th>Description</th></tr></thead><tbody><tr><td><strong>Auth type</strong></td><td>The authentication runs through a Service Account.</td></tr><tr><td><strong>JSON credentials</strong></td><td>After downloading the credentials of your Service Account as a JSON file, you can paste the file content here to authenticate the account.</td></tr><tr><td><strong>Regional classifier</strong></td><td>Specifies the <a href="https://cloud.google.com/bigquery/docs/locations">location of your warehouse</a>. </td></tr><tr><td><strong>Dataset</strong></td><td>Dataset used for all table names in your custom queries that are not tied to a specific dataset.</td></tr></tbody></table>

4. Click on "**Test connection"** to ensure that your connection works reliably and save your data source connection with "**Connect**".

An existing connection can be adapted by clicking on the data source connection. You will see the configuration details from the initial setup again. The credentials are not made visible. However, you can overwrite the existing credentials with new ones by activating the toggle **Update JSON credentials**.

Note: You can configure the data source for two different environments, Staging and Production. You can find more information about it [here](broken://pages/FM88Izx8RDNtGtuF6cvH).&#x20;


# Google Sheets

You can directly connect to your Google Drive to explore your existing spreadsheets, combine them with other data sources and build apps on top of them as a data foundation.

## Connecting your Google Drive

To connect your Google Drive, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **Google Sheets** from the list of available data sources.
3. Click on "**Sign in with Google**" and confirm the access request to Google Drive. After clicking "**Allow**" you will be redirected back to the configuration page.
4. We load the names of all Google Sheets that exist in your Google Drive. In the select menu, you can **select** all Google Sheet **files** that you want to give access to. Note: We enforce the access restriction in our backend. So any query done to GDrive from the frontend will be checked by the backend to make sure there is no breach.
5. Save your Google Drive connection with "**Connect**".

Note: You can configure the data source for two different environments, Staging and Production. You can find more information about it [here](broken://pages/FM88Izx8RDNtGtuF6cvH).&#x20;

{% hint style="info" %}
You need to have edit permissions to use a spreadsheet in Uify. The list of files presented to you for sheet selection already applies that filter. In case you do not find a spreadsheet that you see in your Google Drive, please make sure that you have at least the Editor-role for that sheet. Edit permissions are required because Uify inserts specially constructed formulas for certain operations (e.g. primary key lookups, record counts, etc.). A hidden sheet is added to your spreadsheet to perform such formula operations.
{% endhint %}

## Query your Google Sheets

Once you have connected your Google Drive to Uify and whitelisted the Google Sheet files that you want to allow access to, you can write queries to interact with your data source.

You create queries in your app editor. For that you first need to create an app and then click on "Edit" to open up the editor. Once in the editor, you open the action panel. To launch the action panel you click on the middle icon in the header. To create your query, you click on "**+**" right of the search and then select "**Google Sheets query**".&#x20;

<figure><img src="/files/8AYjeyKfYfNIRZeU3NLp" alt=""><figcaption></figcaption></figure>

First of all, you need to **select** your **data source**. That is the exact data source that you connected in data management in the first place. Next, you select the **type of query** you want to create, i.e. what do you want your Google Sheet to do. There are 6 different types of queries.

### Query Types

#### List spredsheets

It returns the `id` and `name` of Google Sheets files in your Google Drive.

You don't need any additional inputs for this query.

This is an example return value:

```javascript
[{
    id: "1x5Fl0VCL6niHW5qGHxnOpobwHRCGN5bnnl9rCAAJs50",
    name: "Orders"
},
{
    id: "1qdvv58m91TWjCjbaGG6qSL_FB6NwhoFEzRGdskHANYg",
    name: "SKUs"
}]
```

#### **Fetch range(s)**

For a given range, it returns the content in the range. It excludes all empty cells after the last cell that contains a value (vertically and horizontally).

There the query expects **three inputs**: spreadsheet ID, value range(s) and value formatting. You'll find [more info](#inputs) here.

Let's look at an example for the following table, the range `A1:F13` and formatted original values.

<figure><img src="/files/BUqQIhPp6BBX5ZSwnRNp" alt=""><figcaption></figcaption></figure>

The return value would be the following:

```javascript
[
    ["", "Product", "Price"],
    ["", "iPhone", 899],
    ["", "MacBook", 1599],
    ["", "", ""],
    ["", "", "", "", "Contact"],
    ["", "", "", "", "til@example.com"]    
]
```

#### Fetch table(s)

This query returns a table starting from a specific cell. The width of the table is determined by the header column, i.e. a contiguous sequence of cells with values starting from the "left-most header cell".

There the query expects **three inputs**: spreadsheet ID, the left-most header cell and value formatting. Additionally, you can provide the optional return column types. You'll find [more info](#inputs) here.

Let's look at an example for the following table, the left-most header cell `B2`  and formatting as original values.

<figure><img src="/files/BUqQIhPp6BBX5ZSwnRNp" alt=""><figcaption></figcaption></figure>

The return value would be the following:

```javascript
{
    headers: ["Product", "Price"],
    records: [
        ["iPhone", 899],
        ["MacBook", 1599],
        ["", 2499]
    ]
}
```

#### Append to table

This is a create operation. It will add the content specified in the body to your spreadsheet.

The query expects **four inputs**: spreadsheet ID, the left-most header cell, value formatting and the primary key header. The return column types is again optional. You'll find [more info](#inputs) here.

Besides these four inputs, it requires you to provide the record that should be added in the **body**. You can provide an object specifying the row content or an array of objects. If you don't specify the values for all headers or the object includes wrong headers, only matching headers will be filled out. Note: The primary key always needs to be provided!

A sample body could look like this:

```javascript
[
    {
        "Product": "Airpods",
        "Price": 249
    }
]
```

#### Update table records

This is an update operation. It will update an existing record in your spreadsheet.

The query expects **four inputs**: spreadsheet ID, the left-most header cell, value formatting and the primary key header. You can specify if you want the updated record to be returned by the query after the update. If that is the case, you can also set the return column types. You'll find [more info](#inputs) here.

Besides the inputs, you need to provide the new record content that should be written in the spreadsheet. You provide it in the **body**. Similar to the append operation, you provide an object or an array of objects. Only the cells with headers that are mentioned in the object and match the column headers will be updated. Note: The primary key always needs to be provided.

A sample body could look like this (with primary key "Product"):

```javascript
[
    {
        "Product": "MacBook",
        "Price": 1999
    }
]
```

#### Delete table records

This is a delete operation. It will delete an existing record in your spreadsheet specified by the primary key.

The query expects three inputs: spreadsheet ID, the left-most header cell and the primary key header. You'll find [more info](#inputs) here.

Besides these inputs, you need to provide the primary keys of the records you wish to delete. The input takes a single primary key or an array of primary keys.

A sample body for our example spreadsheet with primary key "Product" could be:

```javascript
[
    "MacBook",
    "iPhone"
]
```

### Inputs

For ech query you need to provide a series of inputs. Here is a list of all inputs with an explanation:

<details>

<summary>Spreadsheet ID</summary>

&#x20;A unique identifier of the spreadsheet that you try to fetch from. You can either run the "List spreadsheet" query to get the spreadsheet id or you will find it in the url of the Google Sheet: <https://docs.google.com/spreadsheets/d/><mark style="color:green;">1x5Fl0VCL6niHW5qGHxnOpobwHRCGN5bnnl9rCAAJs50</mark>/edit#gid=0

</details>

<details>

<summary>Value range</summary>

The range from which the content should be fetched. It expects the cell range as [A1 notation](https://developers.google.com/sheets/api/guides/concepts#expandable-1). You can target a value range in a specific sheet by adding the sheet name to the front. E.g.: `Sheet1!A1:B6`

</details>

<details>

<summary>Left-most header cell</summary>

The left starting cell of the header of the table you want to identify in the sheet. It expects the cell in [A1 notation](https://developers.google.com/sheets/api/guides/concepts#expandable-1). You can target a cell in a specific sheet by adding the sheet name to the front. E.g.: `Sheet1!B2`

</details>

<details>

<summary>Value formatting</summary>

* **Original value**: Returns the original value of the cell. I.e. a numeric value will be returned as a number.
* **Formatted string**: Returns the values as strings. I.e. a numeric value will be returned as a string.
* **Formula**: Returns the original value of the cell and for calculated cells the formula as a string.

</details>

<details>

<summary>Primary key header</summary>

Specifies the name of the column header in the spreadsheet that uniquely identifies each row. The input field expects a string.

The primary key is needed for the append query to check if a record with the provided key already exists and for the update and delete queries to identify the records that should be mutated.

E.g. If you have a table with columns `id`, `name` and `email`, the `id` could be the unique identifier.

</details>

<details>

<summary><strong>(optional) Return column types</strong></summary>

For some native values Google returns some unusual values. A date, for example, is by default returned as a numeric value. This input field allows you to transform the values of certain columns to your desired type. It expects an object with the headers as keys and the types as values.&#x20;

E.g.: `{"createdAt": "datetime", "checked?": "boolean"}`

The supported types are:

* `number`
* `string`
* `boolean`
* `json`
* `date`
* `datetime`

</details>


# Microsoft SQL

You can connect to your MSSQL database to explore data, combine them with other data sources and build apps on top of them as a data foundation.

## Connecting to your MySQL Database

To connect your MSSQL database, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **MSSQL** from the list of available data sources.
3. Name your data source connection and provide the connection details:

<table><thead><tr><th width="201">Setting</th><th>Description</th></tr></thead><tbody><tr><td><strong>Hosts</strong></td><td>The host url of the server the database runs on.</td></tr><tr><td><strong>Port</strong></td><td>The port number through which the requests should run through. By default we set the port to 1433 since that's the usual port used for MSSQL database requests.</td></tr><tr><td><strong>Database name</strong></td><td>The name of the database on the host that you wish to connect to.</td></tr><tr><td><strong>Username &#x26; Password</strong></td><td>These are the credentials of the user account that has the required access permissions for the database.</td></tr><tr><td><strong>Connect using SSL</strong></td><td>You can specify if you want to connect to the server using SSL. This is a default setting to ensure secure connection.</td></tr><tr><td><strong>(optional) Use SSH tunnel</strong></td><td><p>Specifies if the connection should run via SSH tunnelling. If you are familiar with SSH tunnelling, you will need to provide the following additional details. Once this option is toggled, we provide our public key for download.</p><ul><li>Bastian host</li><li>Bastian port</li><li>SSH user</li></ul></td></tr></tbody></table>

4. Click on "**Test connection"** to ensure that your connection works reliably and save your data source connection with "**Connect**".

Note: You can configure the data source for two different environments, Staging and Production. You can find more information about it [here](broken://pages/FM88Izx8RDNtGtuF6cvH).&#x20;


# MongoDB

You can connect to your MongoDB database to explore data, combine them with other data sources and build apps on top of them as a data foundation.

## Connecting to your MongoDB Database

To connect your MongoDB database, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **MongoDB** from the list of available data sources.
3. Name your data source connection and provide the connection details:

<table><thead><tr><th width="201">Setting</th><th>Description</th></tr></thead><tbody><tr><td><strong>Hosts</strong></td><td>The host url of the MongoDB instance that stores your database. </td></tr><tr><td><strong>(optional) Use DNS seed list</strong></td><td>You can specify if you want to use DNS entries to provide a seed list for the initial discovery of the replica set configuration. For <a href="https://www.mongodb.com/docs/manual/reference/connection-string/#std-label-connections-dns-seedlist">more info</a>.</td></tr><tr><td><strong>(optional) Use Replica set</strong></td><td><p>You can specify if you want to connect to a MongoDB server that stores a replice of your data. </p><ul><li>Replica set: The host url of your replica server.</li></ul></td></tr><tr><td><strong>Database name</strong></td><td>Name of the database that holds the collections that you want to access.</td></tr><tr><td><strong>Username &#x26; Password</strong></td><td>These are the credentials of the user account that has the required access permissions for (parts of) the database.</td></tr><tr><td><strong>Connect using SSL</strong></td><td>You can specify if you want to connect to the server using SSL. This is a default setting to ensure secure connection.</td></tr><tr><td><strong>(optional) Use SSH tunnel</strong></td><td><p>Specifies if the connection should run via SSH tunnelling. If you are familiar with SSH tunnelling, you will need to provide the following additional details. Once this option is toggled, we provide our public key for download.</p><ul><li>Bastian host</li><li>Bastian port</li><li>SSH user</li></ul></td></tr></tbody></table>

4. Click on "**Test connection"** to ensure that your connection works reliably and save your data source connection with "**Connect**".

Note: You can configure the data source for two different environments, Staging and Production. You can find more information about it [here](broken://pages/FM88Izx8RDNtGtuF6cvH).&#x20;


# MySQL

You can connect to your MySQL database to explore data, combine them with other data sources and build apps on top of them as a data foundation.

## Connecting to your MySQL Database

To connect your MySQL database, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **MySQL** from the list of available data sources.
3. Name your data source connection and provide the connection details:

<table><thead><tr><th width="201">Setting</th><th>Description</th></tr></thead><tbody><tr><td><strong>Hosts</strong></td><td>The host url of the server the database runs on.</td></tr><tr><td><strong>Port</strong></td><td>The port number through which the requests should run through. By default we set the port to 3306 since that's the usual port used for MySQL database requests.</td></tr><tr><td><strong>Database name</strong></td><td>The name of the database on the host that you wish to connect to.</td></tr><tr><td><strong>Username &#x26; Password</strong></td><td>These are the credentials of the user account that has the required access permissions for the database.</td></tr><tr><td><strong>Connect using SSL</strong></td><td>You can specify if you want to connect to the server using SSL. This is a default setting to ensure secure connection.</td></tr><tr><td><strong>(optional) Use SSH tunnel</strong></td><td><p>Specifies if the connection should run via SSH tunnelling. If you are familiar with SSH tunnelling, you will need to provide the following additional details. Once this option is toggled, we provide our public key for download.</p><ul><li>Bastian host</li><li>Bastian port</li><li>SSH user</li></ul></td></tr></tbody></table>

4. Click on "**Test connection"** to ensure that your connection works reliably and save your data source connection with "**Connect**".

Note: You can configure the data source for two different environments, Staging and Production. You can find more information about it [here](broken://pages/FM88Izx8RDNtGtuF6cvH).&#x20;


# OpenAI

With our new OpenAI integration, we allow you to unleash the power of AI in your day-to-day processed to auto-generate content, responses, tag data or extract structured data from text.

## Connecting to your MySQL Database

To connect your MySQL database, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **OpenAI** from the list of available data sources.
3. Name your data source connection and provide the connection details:

<table><thead><tr><th width="201">Setting</th><th>Description</th></tr></thead><tbody><tr><td><strong>API key</strong></td><td>Your unique API key to link your OpenAI account to your Uify apps. Your API key can be found <a href="https://platform.openai.com/account/api-keys">here</a>.</td></tr><tr><td><strong>Organization ID</strong></td><td>If your company has an account of their own, you can provide the org id to count requests towards the organization API quota instead of your account's quota.</td></tr></tbody></table>

4. Click on "**Test connection"** to ensure that your connection works reliably and save your data source connection with "**Connect**".

Note: You can configure the data source for two different environments, Staging and Production. You can find more information about it [here](broken://pages/FM88Izx8RDNtGtuF6cvH).&#x20;

## Action modes

Like any other integration, you can use the OpenAI integration by creating an OpenAI action in your  editor.

We support several modes from a highly customizable manual mode which allows you to specify all aspects of the model inference to use-case specific modes like the "Text to structured data" mode that only asks you for the most important inputs to handle the task out of the box. There is one subpage for each mode.


# Manual mode (chat)

The manual chat mode is a thin layer on top of the OpenAI API's chat completion endpoint. The chat completion allows the user to keep track of the entire chat history and make the model context aware. This mode allows you to customize all aspects of the model inference.

{% hint style="info" %}
This endpoint supports the most recent models of OpenAI. [Click](https://platform.openai.com/docs/models/model-endpoint-compatibility) here for a list of supported models.
{% endhint %}

* **Model**: You can select any model from OpenAI that supports the `chat/completions` endpoint. A list of supported models can be found [here](https://platform.openai.com/docs/models/model-endpoint-compatibility). We recommend to use `gpt-4` if you have access. It is the latest model that supports this endpoint.
* **Max tokens**: This defines the number of tokens in the output of the model. Depending on the model, there is a limit to the number of tokens that a model can take as input from the prompt and the output of the model, i.e. the number of tokens in the input + max tokens should not exceed this limit.
* **Temperature**: The temperature defines the variability/creativity of the model's response. It's value lies between -2 and 2.
* **Frequency penalty and presence penalty**: Both of these parameters together determine how repetitive the answer is. This includes repetitions in words and the content of the text. A positive presence penalty penalizes repetitive tokens while a positive frequency penalty decreases the likelihood that the model repeats it's verbalism. More info can be found [here](https://platform.openai.com/docs/api-reference/parameter-details).
* **Messages**: This is basically the content of the chat. It expects a list of messages the record the chat conversation thus far. Each message consists of two compulsory and two optional parameters:

  * **`role`**: The role can be `system`, `user`, `assistant` or `function`. It represents the entity that engaged in this message. When using `system`, you can define the nature of the assistant. It is like a description of the assistants characteristics. `user` is, naturally, a message by the user/human on the other end. `assistant` is used when the model gives a response. And finally, `function` is the set as role if the model decides to call a function. It appears in combination with `name` , containing the name of the function, and `function_call,` containing the parameters with which a function should be invoked.
  * **`content`**: A string representing the content of the message.
  * **`name`**: You can provide a max. 64 character name of the author. In case, role is `function` this is **not optional**. You have to provide the name of the function that responded in this message.
  * **`function_call`**: The name and the argument of a function that should be called. Since functions are usually invoked by the system, these inputs are generated by the model.

  This is how a list of messages could look like:

  <pre class="language-json" data-overflow="wrap"><code class="lang-json"><strong>[
  </strong>  {
      "role": "system", 
      "content": "You are Dwight Schrute. Answer in a short-tempered manner."
    }, {
      "role": "user", 
      "content": "Hello!"
    } , {
      "role": "assistant", 
      "content": "What?"
    } , {
      "role": "user", 
      "content": "Question, what kind of bear is best?"
    }
  ]
  </code></pre>

  Sometimes you don't want a chat format but you simply want your app to handle individual prompts without any knowledge of prior interactions, i.e. it is stateless. In these cases, you can simply provide a list with a single message:

  ```json
  [  
    {
      "role": "user", 
      "content": "Write a poem about the Berlin summer"
    }
  ]
  ```
* **Functions**: This is fully optional. This is a list of functions that the model can use (i.e. generate JSON inputs for).
  * **`name`**: This is the name of the function with a max. of 64 characters. It should correlate with the name of a function provided in the messages if the role is `function`.
  * **`description`**: This is optional. You can provide a description to the model to make it easier to comprehend what the function can do.
  * **`parameters`**: Expects the json format of the parameters. For more info on the data structure, read through [OpenAI's guide on function calling](https://platform.openai.com/docs/guides/gpt/function-calling).
* **Function Call**: This is, once again, a fully optional property. There are three possible values:
  * `none`: The model does not call a function.
  * `auto`: the model can pick any provided function.
  * `{name: "my_function"}`: The model has to call the provided function.


# Manual mode (complete)

The manual completion mode is a thin layer on top of the OpenAI API's completion endpoint. The completion endpoint returns an answer/a completion for a prompt that the user provides. The mode allows you to customize all aspects of the model inference.

{% hint style="info" %}
The completion endpoint is considered legacy by OpenAI. [Click](https://platform.openai.com/docs/models/model-endpoint-compatibility) here for a list of supported models. The underlying versions are GPT-3.5 and before.
{% endhint %}

* **Model**: You can select any model from OpenAI that supports the `completions` endpoint. A list of supported models can be found [here](https://platform.openai.com/docs/models/model-endpoint-compatibility). We recommend to use `text-davinci-003`. It is the latest model that supports this endpoint.
* **Max tokens**: This defines the number of tokens in the output of the model. Depending on the model, there is a limit to the number of tokens that a model can take as input from the prompt and the output of the model, i.e. the number of tokens in the input + max tokens should not exceed this limit.
* **Temperature**: The temperature defines the variability/creativity of the model's response. It's value lies between -2 and 2.
* **Frequency penalty and presence penality**: Both of these parameters together determine how repetitive the answer is. This includes repetitions in words and the content of the text. A positive presence penalty penalizes repetitive tokens while a positive frequency penalty decreases the likelihood that the model repeats it's verbalism. More info can be found [here](https://platform.openai.com/docs/api-reference/parameter-details).
* **Prompt**: The most important part of the action is the prompt. The prompt contains the instructions for the language model. This could be as simple as "Write a poem about Berlin in the summer." or as complex as "Imagine you are a technical writer for a SaaS company. You write step-by-step guides based on the internal product documentation. Here are examples of your work `{{state.examples}}`. Write an step-by-step guide based on these docs: `{{docs.value}}`:". As you can see, you are free to feed in any dynamic value your app or data source using moustache syntax, i.e. `{{}}`.

<figure><img src="/files/82c8WBv8nYYlbUk0yZmA" alt=""><figcaption></figcaption></figure>


# Text to structured data

This mode is use-case specific. An LLM is good at understand unstructured inputs. Oftentimes, to make use of the LLMs output in a process, you require a structured output. This mode provides just that: It returns a json with a user-defined structure, based on the input.

* **Model**: You can select any model from OpenAI that supports the `chat/completions` endpoint and that you have access to. This includes GPT-4, OpenAI's newest model, however, only if you have already been granted access. More info about that supported models can be found [here](https://platform.openai.com/docs/models/model-endpoint-compatibility).
* **Input**: The input field takes a static or dynamic value that contains the text that should be digested by the model and turned into a json.
* **Describe the input**: A description of what the input is.
* **Output fields**: Output fields are used to describe the structure of the json that will be returned by the model. Each field has a name and a type. At the moment, we support `string`, `number`, `boolean`, `number` and `array`. There needs to be at least one output field.
* **Examples**: Language models are known to perform best if you provide a list of examples. It learns and understands the patterns and applies them to the input. For every example given, you provide an input and the output values based on the structure that was defined in the previous section. Examples are optional but strongly encouraged.

<figure><img src="/files/qKeDiV1kNmrPguMi7GT5" alt=""><figcaption></figcaption></figure>


# Rest API

You can connect to your REST API to request data and interact with 3rd party services or even your own backend. This allows you to combine data with your other data sources and trigger operations based on data of other data source.

## Connecting to your REST API

To connect your REST API, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **REST API** from the list of available data sources.
3. Name your data source connection and provide the connection details:

<table><thead><tr><th width="201">Setting</th><th>Description</th></tr></thead><tbody><tr><td><strong>Base url</strong></td><td>The base url where the API endpoints are placed. You can also provide the entire url for the endpoint if you want to create one data source for each endpoint.</td></tr><tr><td><strong>Timeout</strong></td><td>Length until request times out (to avoid infinitely long requests).</td></tr><tr><td><strong>Params</strong></td><td>Parameters that are passed along with the request.</td></tr><tr><td><strong>Headers</strong></td><td>Headers that are passed along with the request. Especially useful for authentication headers for simple authentication via bearer token.</td></tr><tr><td><strong>(optional) OAuth 2.0 Authentication</strong></td><td><ul><li><strong>Token url</strong>: Url from which to request the access token.</li><li><strong>Client ID &#x26; Secret</strong>: Client credentials (API key and secret) sent as part of the authorization header.</li><li><strong>Scope</strong>: The access scope that you want to request.</li><li><strong>Audience</strong>: Defines the endpoints at which the token can be used. </li></ul></td></tr></tbody></table>

4. Click on "**Test connection"** to ensure that your connection works reliably and save your data source connection with "**Connect**".

Note: You can configure the data source for two different environments, Staging and Production. You can find more information about it [here](broken://pages/FM88Izx8RDNtGtuF6cvH).&#x20;


# Salesforce

You can connect to your Salesforce instance to explore data, combine them with other data sources and build apps on top of them as a data foundation.

## Connecting to your Salesforce instance

To connect your Salesforce instance, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **Salesforce** from the list of available data sources.
3. Click on "**Connect to Salesforce**" and confirm the access request to your instance when redirected to Salesforce. After (potentially logging in and) clicking "**Allow**" you will be redirected back to the configuration page.
4. Save your Salesforce connection with "**Connect**".

Note that, additionally, we allow you to connect to a sandbox instance if you don't want your production database to be linked to Uify. This is especially advisable for the staging environment if you decide to setup a staging and a production environment. You can find more information [here](broken://pages/FM88Izx8RDNtGtuF6cvH) about how the different environments work.&#x20;


# Slack

The Slack API allows you to programmatically trigger messages to individual users or channels.

## Connecting to Slack Workspace

To connect your Slack workspace via its API, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **Slack** from the list of available data sources.
3. Click on "**Connect to Slack**" and confirm the access request to your workspace when redirected to Slack. This will allow you to send messages to the workspace. After clicking "**Allow**" you will be redirected back to the confirmation page.
4. Save your Slack connection with "**Connect**".

Note: You can configure the data source for two different environments, Staging and Production. You can find more information about it [here](broken://pages/FM88Izx8RDNtGtuF6cvH).&#x20;


# Snowflake

You can connect to your Snowflake warehouse to explore data, combine them with other data sources and build apps on top of them as a data foundation.

## Connecting to your Snowflake data warehouse

To connect your Snowflake data warehouse, follow the steps below:

1. Go to **Data Management** and click on "**New source**" to create a new data source.&#x20;
2. Select **Snowflake** from the list of available data sources.
3. Name your data source connection and provide the connection details:

<table><thead><tr><th width="201">Setting</th><th>Description</th></tr></thead><tbody><tr><td><strong>Account identifier</strong></td><td>Unique identifier of your account.</td></tr><tr><td><strong>Warehouse</strong></td><td>Identifier of the warehouse (i.e. the compute engine).</td></tr><tr><td><strong>Database name</strong></td><td>Identifier of the database (i.e. the data storage).</td></tr><tr><td><strong>Schema</strong></td><td>Identifier of the target schema. Defaults to PUBLIC.</td></tr><tr><td><strong>Role</strong></td><td>Role of the user containing the required access privileges. Defaults to PUBLIC.</td></tr><tr><td><strong>Username &#x26; Password</strong></td><td>These are the credentials of the user account that has the required access permissions for (parts of) the data warehouse.</td></tr></tbody></table>

4. Click on "**Test connection"** to ensure that your connection works reliably and save your data source connection with "**Connect**".

Note: You can configure the data source for two different environments, Staging and Production. You can find more information about it [here](broken://pages/FM88Izx8RDNtGtuF6cvH).&#x20;


# Permissions

Uify is built to be used collaboratively. The value of your workspace increases dramatically when apps are shared with other users. To ensure your collaborators can only access what they need to, we have a two-level granular permission system.

## Inviting users

To share access to your workspace, you can invite individual users in your workspace **Settings**. You can access the settings via the dropdown when clicking on your workspace's name. The user management can be found under **Users**. To invite new users, you simply add their email addresses in the uppermost input field. On the right, you can select the workspace role from a dropdown. We differentiate between three different roles:

* **Admin**: Allows the user to do anything in the workspace, including accessing all apps, sharing their access and accessing their editor, managing apps and folders and inviting new users to the workspace and changing their access rights.
* **Creator**: Gives the user the right to access all apps shared with them (acc. to app level permissions), create new apps and access and manage the data management.
* **User**: Can only access apps shared with them (acc. to app level permissions).&#x20;

<table><thead><tr><th width="460">Action</th><th width="93" data-type="checkbox">User</th><th width="101" data-type="checkbox">Creator</th><th data-type="checkbox">Admin</th></tr></thead><tbody><tr><td>See folders with access to at least one app</td><td>true</td><td>true</td><td>true</td></tr><tr><td>Create and manage folders</td><td>false</td><td>true</td><td>true</td></tr><tr><td>Create apps</td><td>false</td><td>true</td><td>true</td></tr><tr><td>Manage data sources</td><td>false</td><td>true</td><td>true</td></tr><tr><td>Access and manage any app</td><td>false</td><td>false</td><td>true</td></tr><tr><td>Manage workspace settings</td><td>false</td><td>false</td><td>true</td></tr><tr><td>Manage users and workspace permissions</td><td>false</td><td>false</td><td>true</td></tr></tbody></table>

Access to the workspace can be revoked by setting a user's status from "Active" to "Deactivated". If a user hasn't answered your invite yet, the status column says "Invite pending". In the dropdown, you can choose to delete the invite or resend it. A user's access level can be changed in the column bearing the same name - you can choose any of the three access roles presented above.

## Sharing apps

Once a user is invited to the workspace, they can be given granular access to your apps. You can access the app-level permissions when clicking on the lock icon in the header of an app or on Permissions in the context menu of an app. You select any workspace user (including users who haven't signed up via their email) from the dropdown to share access to your app and click on **Invite**. Additionally to the access rights of individual users, you can see and set the access level of **All users**. Admins always have full access to all apps in the workspace. This cannot be adapted. Lastly, you can see your own access level.

<figure><img src="/files/PtMnWSFbuYJbyrpVi56m" alt=""><figcaption></figcaption></figure>

There are three access levels for an app:

* **Full access**: Allows the user to access the editor to build or modify apps and share them with other users.
* **Build**: Allows the user to access the editor to build or modify apps but not to share them with other users.
* **Use**: Only allows the user to use the apps. The user will not have access to the editor and cannot share the app with other users.

<table><thead><tr><th width="444">Action</th><th width="90" data-type="checkbox">Use</th><th width="87" data-type="checkbox">Build</th><th data-type="checkbox">Full access</th></tr></thead><tbody><tr><td>Use and see app in navigation</td><td>true</td><td>true</td><td>true</td></tr><tr><td>Comment in app</td><td>true</td><td>true</td><td>true</td></tr><tr><td>Access app editor (incl. previewing and deploying)</td><td>false</td><td>true</td><td>true</td></tr><tr><td>Manage app settings</td><td>false</td><td>true</td><td>true</td></tr><tr><td>Share app and manage access rights</td><td>false</td><td>false</td><td>true</td></tr></tbody></table>


# Shared properties

Every component offers a specific set of properties, events and functions. These are used to modify the look-and-feel and behavior of each component for the particular use case in your app.

While every component type implies a different set of exposed features, there are certain properties that are shared among all components, regardless of the type. As general introduction to the component reference, this page introduces those shared properties first, so that they are not repeated in each reference page again.

### Properties

Every component offers the following configuration options in the properties panel.

<table><thead><tr><th width="193">Property</th><th width="117">Type</th><th width="138">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Box shadow</td><td>Boolean</td><td>Component specific</td><td>When set to <code>true</code>, the affected component is rendered with a surrounding box-shadow. It is enabled by default for some larger components that are usually stand-alone, like a <code>Chart</code>, but disabled for all others. This property is situated in the <code>Style</code> tab of the properties panel.</td></tr><tr><td>Prevent deletion</td><td>Boolean</td><td><code>false</code></td><td>Usually, an app editor can freely delete any component without warning, assuming that there is no active usage. For components that are crucial to your particular app (e.g. a <code>Tabbed Container</code> that acts as app navigation) you might want to enforce confirmation before deletes. When set to <code>true</code>, the affected component cannot be deleted without an explicit confirmation.</td></tr><tr><td>Visible</td><td>Boolean</td><td><code>true</code></td><td>When set to <code>true</code>, the affected component will not be visible in the app for the end user. While editing the app you still see the component, for awareness of its existence and to access its configuration. It will however be rendered semi-transparently to highlight the invisibility setting.</td></tr></tbody></table>

### Events

There are currently no events that all components have in common. Read the reference page of each component to learn more about their events.

### API

Every component exposes the following properties and functions as part of its JavaScript API:

<table><thead><tr><th width="221">Property / Function</th><th width="246">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>componentName</code></td><td><code>string</code></td><td>Type of the component, e.g. <code>button</code> or <code>table</code></td></tr><tr><td><code>name</code></td><td><code>string</code></td><td>The name of the component as defined in the properties panel. This will always equal the variable name of the component in JavaScript code.</td></tr><tr><td><code>setVisible(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the visibility of the affected component to the provided boolean value.</td></tr><tr><td><code>visible</code></td><td><code>boolean</code></td><td>Provides the current value of the "Visible" property, i.e. a boolean value whether the component is visible for the end user or not.</td></tr></tbody></table>


# Button

The `Button` is a fundamental building block for almost every app created with Uify. It renders a clickable button that triggers a pre-configured action on click. There are various styling options to alter the look-and-feel of the button to its particular environment.

![](/files/4V2OgEGVcqwCh2qmg0Pr)

### Properties

The `Button` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Disable while running</td><td><code>boolean</code></td><td><code>false</code></td><td>When set to <code>true</code>, the button is disabled while the on-click action is running. This prevents action executions while previous clicks have not been processed yet.</td></tr><tr><td>Disabled</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether the button should be disabled. When disabled, the button is greyed out and does not trigger the associated on-click action when clicked.</td></tr><tr><td>Show loading while running</td><td><code>boolean</code></td><td><code>false</code></td><td>When set to <code>true</code>, the button text is replaced with a loading spinner while the associated on-click action is executing. This provides clearer visual feedback, especially for long-running actions.</td></tr><tr><td>Text</td><td><code>stringWithJs</code></td><td>"Submit"</td><td>The text displayed on the button.</td></tr><tr><td>Tooltip</td><td><code>stringWithJs</code></td><td>""</td><td>Tooltip text to display on hover over the button. If empty, no tooltip is shown.</td></tr></tbody></table>

### Events

The `Button` has only a single event: "On click". The associated action is executed when the button is clicked by the user, assuming that it is visible and not disabled. The action is executed regardless of how much time has passed between mouse-down and mouse-up.

### API

The `Button` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>click()</code></td><td><code>() => Promise&#x3C;void></code></td><td>Emulates the click on the button. This function is a noop if the button is disabled, however executes the action even if the button is not visible to the user.</td></tr><tr><td><code>disabled</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Disabled" property.</td></tr><tr><td><code>setDisabled(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the value of the "Disabled" property to the provided boolean value.</td></tr><tr><td><code>setValue(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the value of the "Text" property to the provided string.</td></tr><tr><td><code>value</code></td><td><code>string</code></td><td>Provides the current string value of the "Text" property.</td></tr></tbody></table>


# Chart

With the `Chart` component you can easily create graphs or charts to visualize and interpret your data. Our default properties offer enough configurability to quickly create basic chart types like line, area and bar charts. For users with more advanced needs, we added the ability to create fully customizable visualizations by using the [plotly.js API](https://plotly.com/javascript/reference/) with which the theme of the chart can be adapted to your branding and even sophisticated 3-dimensional graphs can be created.

<figure><img src="/files/nygdFy6yPFmh3H1Gnofg" alt=""><figcaption><p>A simple line chart</p></figcaption></figure>

### **Data structure**

The `Chart` component expects one of **three** specific **JSON structures**.&#x20;

In the following, we demonstrate this based on our default data that contains three data series as columns in a table:

| x | y1 | y2 |
| - | -- | -- |
| 1 | -5 | 20 |
| 2 | 20 | 10 |
| 3 | 10 | 5  |
| 4 | 14 | 2  |
| 5 | 9  | 22 |

#### 1. Object of arrays

Each key represents the name of the data series and each value the content of the data series.

```javascript
{
    x: [1, 2, 3, 4, 5],
    y1: [-5, 20, 10, 14, 9],
    y2: [20, 10, 5, 2, 22],
}
```

#### 2. Array of objects

Each object in the array represents a single data point with all it's dimensions.

```javascript
[
    {
        x: 1,
        y1: -5,
        y2: 20
    },
    {
        x: 2,
        y1: 20,
        y2: 10
    },
    {
        x: 3,
        y1: 10,
        y2: 5
    },
    {
        x: 4,
        y1: 14,
        y2: 2
    },
    {
        x: 5,
        y1: 9,
        y2: 22
    }
]
```

#### 3. Table structure

This is the usual structure that is returned for an query action and the very same structure we use for our `Table` component. If you have an action that fetches data right from your data source, you can, thus, simply input `actionName.data` and it will populate your chart component.

```javascript
{
    'headers': ['x', 'y1', 'y2'],
    'records': [
        [1, 2, 3, 4, 5],
        [-5, 20, 10, 14, 9],
        [20, 10, 5, 2, 22],
    ]
}
```

Note that any data series can be chosen as the x-axis value. We will, by default, infer a suitable x-axis value from the provided data for you. Regardless, all data series can be added to the y-axis.

### **Data properties**

<table><thead><tr><th width="181">Property</th><th width="170">Type</th><th width="189">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Data</td><td><code>object</code></td><td>Example as shown in data structures.</td><td></td></tr><tr><td>Type</td><td><code>'Line' | 'Area' | 'Scatter' | 'Bar' | 'Pie'</code> </td><td><code>'Line'</code></td><td>Defines that type of the chart in which the provided data should be presented. Additional chart types can be used when using the Plotly configuration.</td></tr><tr><td>Orientation</td><td><code>'vertical' | 'horizontal'</code></td><td><code>'vertical'</code></td><td>For the bar chart, there is an additional property that defines the orientation of the chart.</td></tr><tr><td>X-Axis field</td><td><code>string</code></td><td><code>'x'</code></td><td>Contains all data series from the provided data. In the dropdown, the data series that should represent the x-axis can be selected. For the pie chart, the x-axis field reflect the labels.</td></tr><tr><td>Data series</td><td><code>array</code></td><td><code>['y1', 'y2']</code></td><td>Represents the data series on the y-axis of the chart. Additional data series can be added via "Add data series". Each data series has some configurable properties themselves (see below).</td></tr></tbody></table>

**Data series properties**

Each data series possesses some properties of their own:&#x20;

<table><thead><tr><th width="181">Property</th><th width="170">Type</th><th width="189">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Name</td><td><code>stringWithJs</code></td><td>inferred</td><td>Name of the data series.</td></tr><tr><td>Y-Axis field</td><td><code>color</code></td><td>inferred</td><td>Contains all data series from the provided data. It allows you to select the data that should be associated with your data series.</td></tr><tr><td>Color</td><td><code>stringWithJs</code></td><td>inferred</td><td>The color of the trace (line, bar, etc.) of your data series.</td></tr><tr><td>Aggregation - Enabled</td><td><code>boolean</code></td><td><code>'false'</code></td><td>For data points that don't have unique x-axis values, this allows you to aggregate them into a single y-axis value. Note: Aggregation is not supported by the pie chart.</td></tr><tr><td>Aggregation - Method</td><td><code>'Count' | 'Sum' | 'Average'</code></td><td><code>'Count'</code></td><td>If aggregation is enabled, this input defines how the data series should be aggregated. E.g. if we choose <code>'Sum'</code>, all data points that have the same x value are summed up and presented as the y-axis value. Note: For non-numeric values, only <code>'count'</code> is avalable as an aggregation method.</td></tr></tbody></table>

As mentioned above, we provide some basic customization options for the user to create their first chart quickly. Beyond that, the user has the choice to fully customize their chart using plotly.js's API. Therefore, we offer a `Mode` switch to quickly switch between our form-based configuration and [Plotly's JSON configuration](https://plotly.com/javascript/reference/) format. When you switch from the form-based input to the JSON configuration, all specifications that were made in the form are synced to/reflected in the JSON.

We recommend to review their [documentation](https://plotly.com/javascript/reference/) to specify exactly what you need.

<details>

<summary>For reference, here is an example of a viable Plotly data configuration:</summary>

<pre class="language-javascript"><code class="lang-javascript"><strong>[
</strong>  {
    type: "scatter",
    mode: "lines+markers",
    name: "y2",
    orientation: "v",
    marker: {
      color: "#E78573"
    },
    x: chart1.formattedData.x,
    y: chart1.formattedData.y2
  },
  {
    type: "scatter",
    mode: "lines+markers",
    name: "y1",
    orientation: "v",
    marker: {
      color: "#9ABEED"
    },
    x: chart1.formattedData.x,
    y: chart1.formattedData.y1
  }
]
</code></pre>

</details>

### **Layout properties**

<table><thead><tr><th width="181">Property</th><th width="160">Type</th><th width="188">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Title</td><td><code>stringWithJs</code></td><td><code>''</code></td><td>Defines the title of the chart.</td></tr><tr><td>Show legend</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines if a legend is displayed in the chart component.</td></tr><tr><td>Legend position</td><td><code>'left' | 'top' | 'bottom' | 'right'</code></td><td><code>'bottom'</code></td><td>If a legend is displayed, this property determines its position.</td></tr><tr><td>X-Axis - Title</td><td><code>stringWithJs</code></td><td><code>''</code></td><td>Defines the title placed next to the x-axis.</td></tr><tr><td>X-Axis - Show labels</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines if the labels are displayed on the x-axis.</td></tr><tr><td>X-Axis - Show grid</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines if the grid (grid lines and axis) is shown for the x-axis.</td></tr><tr><td>Y-Axis - Title</td><td><code>stringWithJs</code></td><td><code>''</code></td><td>Defines the title placed next to the y-axis.</td></tr><tr><td>Y-Axis - Show labels</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines if the labels are displayed on the y-axis.</td></tr><tr><td>Y-Axis - Show grid</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines if the grid (grid lines and axis) is shown for the y-axis.</td></tr></tbody></table>

Users that prefer to customize the layout and appearance of their chart can use [Plotly's JSON configuration](https://plotly.com/javascript/reference/) format. The `Custom` configuration is conveniently accessible via the `Mode` switch. When you switch from the form-based input to the JSON configuration, all specifications that were made in the form are synced to/reflected in the JSON.

We recommend to review their [documentation](https://plotly.com/javascript/reference/) to specify exactly what you need.

<details>

<summary>For reference, here is an example of a viable Plotly layout configuration:</summary>

```javascript
{
  font: {
    family: "Inter",
    color: "#B3B3B3"
  },
  margin: {
    r: 20,
    l: 20,
    b: 20,
    t: 20,
    pad: 5
  },
  hoverlabel: {
    bgcolor: "#FFFFFF",
    bordercolor: "#E1E1E1",
    font: {
      color: "#323232",
      family: "Inter",
      size: 12
    }
  },
  showlegend: true,
  legend: {
    xanchor: "center",
    yanchor: "top",
    orientation: "h",
    x: 0.5,
    y: -0.2
  },
  colorway: [
    "#9ABEED",
    "#E78573",
    "#B8D279",
    "#B481BB",
    "#F9C56A",
    "#9490D7",
    "#9CD2C8",
    "#BCB8D8",
    "#F3B7A6",
    "#F9EE8A",
    "#F4D0E5"
  ],
  xaxis: {
    automargin: true,
    showgrid: true,
    showticklabels: true,
    zeroline: true,
    zerolinewidth: 3,
    zerolinecolor: "#eeeeee"
  },
  yaxis: {
    automargin: true,
    showgrid: true,
    showticklabels: true,
    zeroline: true,
    zerolinewidth: 3,
    zerolinecolor: "#eeeeee"
  }
}
```

</details>

### **Events**

`Chart` does not offer any events.

### **API**

<table><thead><tr><th width="215">Property</th><th width="284">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>formattedData</code></td><td><code>() => object</code></td><td>Returns the data underlying the chart in a unified format, including the x-axis input and all data series. This is required since we allow different structures as data input.</td></tr></tbody></table>


# Checkbox

The `Checkbox` is similar to the [Switch](/component-reference/switch) component, offering the user to make a "Yes or No" decision, but using a checkbox as the UI element instead of a toggle.

![](/files/fk5QaQFE6Hv8gLubtYDz)

### Properties

The `Checkbox` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Default value</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines the initial state of the checkbox, and the state it will have when the <code>reset()</code> <a href="#api">API function</a> is used. When configured with a JS expression, it also updates the checkbox reactively when a dependency changes. <a href="/pages/dQa0dv0ATTUsNMfmJJz9#reactivity">Learn more</a></td></tr><tr><td>Disabled</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether the checkbox should be disabled. When disabled, the checkbox is greyed out and does not trigger the associated events.</td></tr><tr><td>Label</td><td><code>stringWithJs</code></td><td><code>'Checkbox'</code></td><td>Text displayed next to the checkbox.</td></tr><tr><td>Label alignment</td><td><code>'left' | 'center' | 'right'</code></td><td><code>'left'</code></td><td>Alignment of the label text.</td></tr><tr><td>Label position</td><td><code>'left' | 'right'</code></td><td><code>'left'</code></td><td>Whether the checkbox is rendered to the left of the label text, or to the right of it.</td></tr><tr><td>Required</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether this checkbox must be active when a surrounding form is submitted. <a href="/pages/zKC0dhqXeNBI8qzeUEIb#submitting-a-form">Learn more about form submission</a></td></tr><tr><td>Toggle with label click</td><td><code>boolean</code></td><td><code>false</code></td><td>When set to <code>true</code>, the checkbox state is toggled when clicking on the label text as well. Otherwise, only a click on the checkbox itself changes its state.</td></tr></tbody></table>

### Events

The `Checkbox` offers three events related to changing its state. All of these events will only fire when the state changes due to UI events (i.e. user clicks), not when the checkbox state is changed programmatically. None of the associated actions will receive any [arguments](/writing-code/actions/execution-model#action-arguments) when triggered.

* **On change run**: This action is triggered every time the checkbox value changes.
* **On false run**: This action is triggered when the checkbox value changes from `true` to `false`, i.e. when the user unchecks it.
* **On true run**: This action is triggered when the checkbox value changes from `false` to `true`, i.e. when the user checks it.

{% hint style="info" %}
"On false run" and "On true run" are triggered slightly earlier than "On change". They are however not executed in sequence, i.e. triggering the "On change" action does not wait or depend on the execution of "On false" or "On true". Due to this behavior, their executions might overlap.
{% endhint %}

### API

The `Checkbox` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>clear()</code></td><td><code>() => void</code></td><td>Clears the checkbox, i.e. sets its value to <code>false</code> and unchecks it visually.</td></tr><tr><td><code>disabled</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Disabled" property.</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>Provides the current string value of the "Label" property.</td></tr><tr><td><code>reset()</code></td><td><code>() => void</code></td><td>Resets the checkbox state to the initial "Default value", i.e. it is set to the same state that it had after initial app load.</td></tr><tr><td><code>setDisabled(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the value of the "Disabled" property to the provided boolean value.</td></tr><tr><td><code>setLabel(value)</code></td><td><code>(value: string) => void</code></td><td>Updates the value of the "Label" property to the provided string value.</td></tr><tr><td><code>setValue(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the value of the checkbox to the provided boolean value. <code>true</code> checks it, <code>false</code> unchecks it. This does not trigger any actions associated with checkbox events.</td></tr><tr><td><code>value</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the checkbox state. <code>true</code> in case the checkbox is checked, <code>false</code> otherwise.</td></tr></tbody></table>


# Container

The main purpose of the `Container` component is to act as separate host for other child components. It groups a set of components together in a dedicated frame, arranged on a separate grid from the main application canvas. A `Container` can be made scrollable to create a scroll container to place larger content than the available space.

When the dimensions of a `Container` on the canvas are adjusted, its content resizes dynamically.

<figure><img src="/files/JXoqGCWXCG7jofffG9jC" alt=""><figcaption><p>Container content resizes dynamically</p></figcaption></figure>

Use this component to create separate areas in your app for specific component groups. The styling options allows these blocks to stand out visually compared to the rest of the application.

### Properties

The `Container` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Header text</td><td><code>stringWithJs</code></td><td><code>'Header'</code></td><td>When the "Show Header" property is set to <code>true</code>, the text of this property is displayed in the header of the container. This configuration is not accessible in the properties panel otherwise.</td></tr><tr><td>Scrollable</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether the container should allow scrolling in case its content is larger than the container itself. If disabled, overflowing content is hidden and not accessible to the end user.</td></tr><tr><td>Show Header</td><td><code>boolean</code></td><td><code>false</code></td><td>When set to <code>true</code>, the container renders a text header, which can be modified in the "Header text" configuration.</td></tr></tbody></table>

### Scrollable containers

A container with "Scrollable" set to `false` is still scrollable in the app editor, but highlights overflowing content with a red background.

<figure><img src="/files/4kxZ9VV1FztJfbYmLp4w" alt=""><figcaption><p>Warning the user about potentially inaccessible container content</p></figcaption></figure>

The purpose of this is to still allow the app editor to freely modify the container content, while  aware that some content will be inaccessible for the app user. The production version does not  scroll for such a container, and cuts off overflowing content.

### Events

The `Container` does not offer any events.

### API

The `Container` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>headerText</code></td><td><code>string</code></td><td>Current string value of the "Header text" configuration.</td></tr><tr><td><code>setHeaderText(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the header text to the provided string value.</td></tr><tr><td><code>setShowHeader(value)</code></td><td><code>(value: boolean) => void</code></td><td>Adjusts the flag whether the header text should be displayed to the provided boolean value.</td></tr><tr><td><code>showHeader</code></td><td><code>boolean</code></td><td>Current boolean value of the "Show header" configuration.</td></tr></tbody></table>


# Data Grid

The `Data Grid` is a versatile and dynamic way to display tabular data of any size. Unlike the `Table` component, which has a fixed number of columns, the `Data Grid` can adapt to a variable amount of columns. In addition, it allows you to add color highlights to cells, making it easy to draw attention to important data or identify patterns in your information.

<figure><img src="/files/e58QxhiL6MPF83wfkfn6" alt=""><figcaption><p>Example of the Data Grid component with color highlights</p></figcaption></figure>

### Data structure

The `Data Grid` component can handle input data in a variety of structures. Depending on your needs, you can choose to use an object with “headers” and “records” keys, an array of objects, or an array of arrays to populate the `Data Grid` with your data. We’ll cover each of these three options in detail and with examples, so you can choose the one that works best for your specific use case.

We however first need to understand how a single cell value is described across all possible input structures. A cell value can be provided as follows:

```typescript
type GridCellValue = string | {
  html?: string;
  markdown?: string;
  text?: string;
  color?: DataGridColor;
  alignment?: 'left' | 'center' | 'right';
  weight?: 400 | 500 | 600 | 700;
}
```

The most basic way of providing a cell value is expressing it as a simple string. For example, `'city'` is a valid cell value and will be displayed as-is in the grid. The `Data Grid` however allows to describe a cell value with an object structure, in case special formatting should be applied. Firstly, there are three possible objects keys to provide the actual content of a cell:

* `text`: This property expects a string, which will be displayed as-is in the particular cell. This is perfect for cases where a basic textual value should be displayed with additional formatting.
* `markdown`: Text provided via this key will be processed as markdown text. For example, you may provide the string `'*city*'` to render italic text in the grid.
* `html`: With this key, you may provide custom HTML code that will be rendered as cell value. This offers highly customized value rendering for complex use cases.

On top of the content, you may use the following object keys to apply value formatting:

* `color`: This property allows to provide a font-, background- and/or border-color for the particular cell. This mechanism is described in more detail in [this section](#color-highlights).
* `alignment`: This property allows to define the text alignment within the particular cell. Allowed values are `'left'`, `'center'` and `'right'`.
* `weight`: Using this property, you may adjust the font weight of the particular cell. Allowed values are the numbers 400, 500, 600 and 700.

Illustrating this structure with an example, the following object describes the italic text "Hello world", which is center-aligned and has a green background:

```typescript
{
  html: '<i>Hello world</i>',
  alignment: 'center',
  color: 'green',
}
```

After understanding the definition of a single cell value, let us now dive into the three possible structure for defining the data of the entire grid:

#### 1. Object with "headers" and "records" array

This structure is perfect for use cases where you would like to render data from SQL actions in the grid. It is described by the following structure:

```typescript
interface ObjectGridData {
  headers: GridCellValue[];
  records: GridCellValue[][];
}
```

In this structure, the "headers" array will be used to populate the first row, while the arrays in the "records" array are populating the rest of the grid. This allows to funnel the result of data source actions directly into the grid without any further manipulation or transformation. An example of such structure could look as follows:

```typescript
{
  headers: ['city', 'country', 'population'],
  records: [
    ['Tokyo', 'Japan', { text: '37,468,000', alignment: 'center' }],
    ['Delhi', 'India', { text: '28,514,000', alignment: 'center' }],
    ['Shanghai', 'China', { text: '25,582,000', alignment: 'center' }],
    ['Sao Paulo', 'Brazil', { text: '21,650,000', alignment: 'center' }],
    ['Mexico City', 'Mexico', { text: '21,581,000', alignment: 'center' }],
  ],
}
```

#### 2. Array of objects

This structure is most human-readable, as cell values are directly associated with column headers. When providing an array of objects, the object keys will be interpreted as column headers, while the object values will be used as cell values. Using the same example of "city populations" from the previous section, an example of this structure could look as follows:

```typescript
[
  {
    city: 'Tokyo',
    country: 'Japan',
    population: { text: '37,468,000', alignment: 'center' },
  },
  {
    city: 'Delhi',
    country: 'India',
    population: { text: '28,514,000', alignment: 'center' },
  },
  {
    city: 'Shanghai',
    country: 'China',
    population: { text: '25,582,000', alignment: 'center' },
  },
  {
    city: 'Sao Paulo',
    country: 'Brazil',
    population: { text: '21,650,000', alignment: 'center' },
  },
  {
    city: 'Mexico City',
    country: 'Mexico',
    population: { text: '21,581,000', alignment: 'center' },
  },
]
```

#### 3. Array of arrays

The third accepted structure resembles the most with a data grid: an array of arrays. The first array element will be interpreted as header row, while all following arrays will be used as column values. The "array of arrays" version of the previous example will look as follows:

```typescript
[
  ['city', 'country', 'population'],
  ['Tokyo', 'Japan', { text: '37,468,000', alignment: 'center' }],
  ['Delhi', 'India', { text: '28,514,000', alignment: 'center' }],
  ['Shanghai', 'China', { text: '25,582,000', alignment: 'center' }],
  ['Sao Paulo', 'Brazil', { text: '21,650,000', alignment: 'center' }],
  ['Mexico City', 'Mexico', { text: '21,581,000', alignment: 'center' }],
]
```

{% hint style="info" %}
The `Data Grid` component is fairly lenient about missing cell values in the provided structure. For example, in case not all objects in an "array of objects" structure contain a specific key, it will be filled up with an empty cell. Same applies to an "array of arrays" structure, in which the individual array elements have different lengths. In this case, the longest array will be considered and all other rows will be filled up with empty cells.
{% endhint %}

### Color highlights

The [previous section](#data-structure) already introduced the ability to apply formatting to a cell, by expressing the cell value as an object structure. With regards to coloring, you can choose between two alternatives:

1. Use one of the predefined color names
2. Provide custom color codes for background, border and/or font

The first option is simple: you just need to provide one of the color names listed below, and the `Data Grid` will color the background accordingly. An example of defining a cell value with green highlighting may look as follows:

```typescript
{
  text: "Using the predefined 'green' color",
  color: 'green',
}
```

While this is usually enough for most use cases, you may however also define custom colors with the following structure:

```typescript
interface DataGridCustomColors {
  background?: string;
  border?: string;
  text?: string;
}
```

This structure allows you to provide any valid CSS color code as background-, border- and/or text-color. An example of a cell value with white font on black background looks as follows:

```typescript
{
  text: 'White text on black background',
  color: {
    background: '#000',
    text: 'white',
  },
}
```

{% hint style="info" %}
The supported predefined color names are:

* `gray` or `grey`
* `orange`
* `yellow`
* `green`
* `blue`
* `purple`
* `pink`
* `red`
  {% endhint %}

### Legend

The legend in the footer of the grid allows to describe your color highlights. A legend entry always consists of two parts: a color code and a label. The color code is visualized with a small, colored square, while the label is rendered as text next to it. These entries must be provided in a dictionary that maps the label (i.e. value) to the color code (i.e. key).

The following example describes two legend entries that are based on the default color set:

```typescript
{
  green: 'Green highlight',
  red: 'Red highlight',
}
```

You may however also use any valid CSS color, as follows:

```typescript
{
  '#000': 'Black highlight',
  'rgb(0, 0, 0)': 'Another black highlight',
}
```

### Properties

The `Data Grid` component offers the following configuration properties.

<table><thead><tr><th width="174">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Column widths</td><td><code>number | number[]</code></td><td><code>[240, 90]</code></td><td>Sets the widths in pixels of the grid columns. In case one number is provided, all columns will have the given width. An array of numbers will apply these numbers to the columns in order, and all following columns will use the last array element.</td></tr><tr><td>Data</td><td><code>GridData</code></td><td>Example data</td><td>Defines the data that is displayed in the grid. Please refer to the <a href="#data-structure">Data structure</a> section for more details on accepted structures.</td></tr><tr><td>Data export</td><td><code>boolean</code></td><td><code>true</code></td><td>When set to <code>true</code>, the user will be able to export the displayed data to CSV or XLSX, by using the download-icon in the footer.</td></tr><tr><td>First row is header</td><td><code>boolean</code></td><td><code>true</code></td><td>When set to <code>true</code>, the first row is displayed with bold font, and behaves sticky when the grid is scrolled vertically.</td></tr><tr><td>Fixed column count</td><td><code>number</code></td><td><code>1</code></td><td>Defines the number of columns which are fixed and therefore behave sticky when the grid is scrolled horizontally.</td></tr><tr><td>Header background color</td><td><code>string</code></td><td><code>white</code></td><td>Background color of the header row. Only relevant when "First row is header" is set to <code>true</code>.</td></tr><tr><td>Header text color</td><td><code>string</code></td><td><code>black</code></td><td>Text color in the header row. Only relevant when "First row is header" is set to <code>true</code>.</td></tr><tr><td>Legend</td><td><code>Record&#x3C;string, string></code></td><td>Example legend</td><td>Defines the legend entries that will be displayed in the footer. Should be a mapping from "color code" to "color label". See more details in the <a href="#legend">Legend</a> section.</td></tr><tr><td>Text color</td><td><code>string</code></td><td><code>black</code></td><td>Font color in the grid, except the header row.</td></tr></tbody></table>

### Events

The `Data Grid` does not offer any events.

### API

The `Data Grid` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>columnWidths</code></td><td><code>number | number[] | null</code></td><td>Current value of the "Column widths" property.</td></tr><tr><td><code>data</code></td><td><code>GridData | null</code></td><td>Current value of the "Data" property, please refer to the <a href="#data-structure">Data</a> section for more details.</td></tr><tr><td><code>dataExport</code></td><td><code>boolean</code></td><td>Current value of the "Data export" property.</td></tr><tr><td><code>firstRowIsHeader</code></td><td><code>boolean</code></td><td>Current value of the "First row is header" property.</td></tr><tr><td><code>fixedColumnCount</code></td><td> <code>number | null</code></td><td>Current value of the "Fixed column count" property.</td></tr><tr><td><code>legend</code></td><td><code>Record&#x3C;string, string> | null</code></td><td>Current value of the "Legend" property.</td></tr><tr><td><code>setColumnWidths(value)</code></td><td><code>(value: number | number[] | null) => void</code></td><td>Sets the column widths to the provided number(s) in pixels.</td></tr><tr><td><code>setData(value)</code></td><td><code>(value: GridData) => void</code></td><td>Sets the displayed data to the provided structure. See the <a href="#data-structure">Data</a> section for more details about the expected structures.</td></tr><tr><td><code>setDataExport(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the data export flag to the provided value.</td></tr><tr><td><code>setFirstRowIsHeader(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the first-row-is-header flag to the provided value.</td></tr><tr><td><code>setFixedColumnCount(value)</code></td><td><code>(value: number | null) => void</code></td><td>Sets the fixed column count to the provided value.</td></tr><tr><td><code>setLegend(value)</code></td><td><code>(value: Record&#x3C;string, string> | null) => void</code></td><td>Sets the legend to the provided value. See the <a href="#legend">Legend</a> section for more details.</td></tr></tbody></table>


# Form

The `Form` component allows grouping of multiple input-components, for example, text inputs, selects, checkboxes or switches, together in one coherent form. This component can host other child components and has its own grid. Creating forms like this has the following benefits:

* The `Form` automatically contains a submit button, and can be configured to offer a secondary cancel-button
* Validation rules are applied to all inputs of the form when it is submitted
* All inputs of the form can be automatically cleared after a successful submit

The following sections describes these mechanisms in more detail.

<figure><img src="/files/MQ9Ud7zFjrd4yBtM2iNY" alt=""><figcaption><p>Form component in action</p></figcaption></figure>

### Validation rules

Input components may have validation rules. These could be simple, for example, "minimum text length". Or more sophisticated, for example, custom regular expressions or even custom JavaScript actions. These validations are not applied for stand-alone components, but they are considered when a surrounding `Form` is submitted.&#x20;

The `Form` component offers a special "Validate on submit" configuration option for this purpose. If this property is set to `true`, all validation rules of all child components will be evaluated, including those of nested containers (e.g. a `Form`, containing a `Container`, containing inputs). If even a single validation rule fails, the form submission is aborted and errors are highlighted for all affected child components. In addition, the "Required" property of all child input components is evaluated as well. Any child input with "Required" set to `true`, but not holding a value, also leads to a validation error.

<figure><img src="/files/Qad0I4O613yd1PWLyxNX" alt=""><figcaption><p>Submitted form with a child component with validation error</p></figcaption></figure>

In case "Validate on submit" is disabled, no validation rules will be evaluated and the form submission will proceed without any data checks.

### Submitting a form

The `Form` component associates an action with the "On submit" event. This action will be executed when the submit button is pressed, and no validation error has occurred ([see above](#validation-rules)).&#x20;

The form submission goes through the following steps:

1. In case the "Disable while submit" property is set to `true`, all child input components, the submit button and the cancel button will be disabled. Especially for long-running submit actions, this guarantees that the user is not able to modify any inputs while a submission is still ongoing, or triggering more submits.
2. If the "Show loading while submit" property is set to `true`, the submit button and cancel button render a loading spinner while the submit action is running, instead of their usual button labels.
3. The submit action is triggered, without any [arguments](/writing-code/actions/execution-model#action-arguments). All subsequent steps happen only once this action run has finished.
4. In case the submit action finished successfully, and the "Clear after submit" property is set to `true`, all child input components are emptied. This makes it easier to have an empty form after a successful submission.
5. Previously disabled child input components and form buttons are now enabled again, and the buttons render their labels again instead of the loading spinner. This happens even if the submit action fails with an error.

{% hint style="info" %}
In some cases, the submit action will not fail with an error, but you do not want to clear the form. This can be achieved by returning `false` in the submit action. In this case, clearing the form is skipped even when "Clear after submit" is set to `true`.<br>

You must explicitly return `false`, and not something falsy like 0, null, undefined or an empty string.
{% endhint %}

<figure><img src="/files/EP8W4MM7ymLjo6OJeklW" alt=""><figcaption><p>Different form settings on submit</p></figcaption></figure>

### Getting form data

Retrieving all values in a form, or filling a form with data, are common operations in many apps. Using getters and setters of each individual child input is possible, but cumbersome. For this reason, the `Form` component offers a convenient getter and setter for all child inputs at once.&#x20;

For example, a form called `addressForm` has three text inputs: `nameInput`, `streetInput` and `cityInput`. Typically, you retrieve data with the following code:

```typescript
const nameValue = nameInput.value;
const streetValue = streetInput.value;
const cityValue = cityInput.value;
```

A more convenient way is the following:

```typescript
const formData = addressForm.getData();
// formData will be an object like this:
// {
//   nameInput: string;
//   streetInput: string;
//   cityInput: string;
// }
```

The result is fully typed, so that you can enjoy helpful IntelliSense support:

<figure><img src="/files/pJAKWjWGCXue3F79fyYP" alt=""><figcaption><p>Full type support for form data getter</p></figcaption></figure>

Fetching all values of all inputs like this is useful, but sometimes ambiguous. There are two categories of input components for which the question "What is your value?" could be answered in different ways:

* `Select` / `Multi-Select`: These components offer two ways to retrieve their value(s): `value` and `values`.&#x20;
  * `values` provides an array of all selected values (i.e. an empty array in case nothing is selected).
  * `value` provides the first selected value as primitive (i.e. `null` in case nothing is selected).
* `Date` / `Date-Time` / `Time`: These components offer three ways to retrieve their value:
  * `value`: returns a JS `Date` or `null`
  * `valueISOString`: returns an ISO 8601 string or `null`
  * `valueFormattedString`: returns a string formatted according to the configuration properties of the input, or `null`

Due to this ambiguity, there is a way to explicitly declare what format to receive the data in. The `getData()` function of the `Form` API accepts an object argument to define the format for each individual child input:

```typescript
const formData = addressForm.getData({
  countrySelect: {
    getterName: 'values'
  },
  registrationDate: {
    getterName: 'valueISOString'
  }
});
// formData.countrySelect is now guaranteed to be an array
// formData.registrationDate will be a string in ISO 8601 format, or null
```

For convenience, using this function is fully supported by the Uify type system.

<figure><img src="/files/GhbmLZusYbCqIqXLoVyk" alt=""><figcaption><p>Type support for providing format hints</p></figcaption></figure>

### Setting form data

Filling an entire form with a single method call can be done by using the `setData()` function of the `Form` API. It accepts an object argument, with names of the targeted child inputs as keys, and the values you would like to set for them as object values. Continuing the previous example of the `addressForm`, add the following:

```typescript
addressForm.setData({
  nameInput: 'John Doe',
  streetInput: 'Main Street 1',
  cityInput: 'New York'
});
```

Similar to the type support of `getData()`, the Uify IDE offers supports when constructing such expressions:

<figure><img src="/files/BZ7KuvcC6RmD7L0k2oBt" alt=""><figcaption><p>Type support when using setData()</p></figcaption></figure>

This type support also prevents providing wrong input values (in this example, a `number` instead of the expected `string`). This validation depends on the input type.

* A `Select` allows the pass of an array of selected values.
* `Date Input` accepts a JS `Date`.&#x20;

{% hint style="warning" %}
Besides the better usability, using `setData` is also more performant than using individual setters of each input. `setData`is executed in a single, atomic state update and is synchronized to other collaborators in a single message. Using multiple setters of specific input components. however, results in a sequence of updates and messages. It is therefore strongly recommended to use the form getters and setters when possible.
{% endhint %}

### Properties

The `Button` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Button disabled</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether the submit button should be disabled or not.</td></tr><tr><td>Cancel button disabled</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether the secondary cancel button should be disabled or not. This property is only accessible when "Show cancel button" is set to <code>true</code></td></tr><tr><td>Cancel button text</td><td><code>stringWithJs</code></td><td><code>'Cancel'</code></td><td>Label of the secondary cancel-button. This property is only accessible when "Show cancel button" is set to <code>true</code></td></tr><tr><td>Clear after submit</td><td><code>boolean</code></td><td> <code>true</code></td><td>When set to <code>true</code>, all child inputs of the form are cleared after successful execution of the "On submit" action - unless it returns <code>false</code>. <a href="#submitting-a-form">Learn more</a></td></tr><tr><td>Disable while submit</td><td><code>boolean</code></td><td><code>false</code></td><td>When set to <code>true</code>, all child inputs, the submit-button and the cancel-button are temporarily disabled while the "On submit" action is running. <a href="#submitting-a-form">Learn more</a></td></tr><tr><td>Full width</td><td><code>boolean</code></td><td><code>false</code></td><td>When set to <code>true</code>, the submit and cancel button will take the entire width of the form, instead of just enough space to contain their labels.</td></tr><tr><td>Header text</td><td><code>stringWithJs</code></td><td><code>'Header'</code></td><td>The text to be displayed in the form header. This property is only accessible when the "Show header" property is set to <code>true</code></td></tr><tr><td>Hide submit button</td><td><code>boolean</code></td><td> <code>false</code></td><td>When set to <code>true</code>, the submit button is not displayed at all. This is relevant for use cases where a form should only be submitted programmatically.</td></tr><tr><td>Scrollable</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether the user can scroll to overflowing content, or whether that content will  be hidden.</td></tr><tr><td>Show cancel button</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether the secondary cancel button should be displayed next to the submit button or not.</td></tr><tr><td>Show header</td><td><code>boolean</code></td><td><code>false</code></td><td>When set to <code>true</code>, the form will show a textual header, that can be configured with the "Header text" property.</td></tr><tr><td>Show loading while submit</td><td><code>boolean</code></td><td><code>true</code></td><td>When set to <code>true</code>, both the submit and cancel button display a loading spinner while the "On submit" action is running instead of their labels. <a href="#submitting-a-form">Learn more</a></td></tr><tr><td>Text</td><td><code>stringWithJs</code></td><td><code>'Submit'</code></td><td>The label of the submit button.</td></tr><tr><td>Validate on submit</td><td><code>boolean</code></td><td><code>true</code></td><td>When set to <code>true</code>, all validation rules and "Required" properties of child inputs will be evaluated. In case of any validation error, the form submission will be aborted and errors will be rendered for all affected inputs. <a href="#validation-rules">Learn more</a></td></tr></tbody></table>

{% hint style="info" %}
Read the documentation of the [Container component](/component-reference/container#properties) to learn more about the behavior of the "Scrollable" property, and how it differs in the app editor versus the end user experience in the workspace.
{% endhint %}

### Events

The `Form` allows to define actions to be executed in case of two events:

* **On submit run**: This action is executed when a form is submitted, i.e. when either the user clicks on the non-disabled submit button, or when the `submit()` function is triggered programmatically. The execution time of this action determines how long the form might be disabled and spinners might be displayed. When this action returns `false`, the automatic form clearing can be prevented ([learn more](#submitting-a-form)).
* **On cancel run**: This action is executed when the user clicks on the non-disabled, secondary cancel button. For this to be possible, the "Show cancel button" property must be set to `true`.

### API

The `Form` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>buttonColor</code></td><td><code>string</code></td><td>Provides the color of the submit button.</td></tr><tr><td><code>buttonDisabled</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Button disabled" property, i.e. whether the submit button is currently disabled or not. This does not take into account potential disabling during submit.</td></tr><tr><td><code>buttonFullWidth</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Full width" property.</td></tr><tr><td><code>buttonText</code></td><td><code>string</code></td><td>Provides the submit-button label, i.e. the current value of the "Text" property.</td></tr><tr><td><code>buttonTextColor</code></td><td><code>string</code></td><td>Provides the text-color of the submit button.</td></tr><tr><td><code>cancelButtonDisabled</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Cancel button disabled" property, i.e. whether the cancel button is currently disabled or not. This does not take into account potential disabling during submit.</td></tr><tr><td><code>cancelButtonText</code></td><td><code>string</code></td><td>Provides the cancel button label, i.e. the current value of the "Cancel button text" property.</td></tr><tr><td><code>clear()</code></td><td><code>() => void</code></td><td>Clears all form child inputs, i.e. sets their value to "empty". The semantics of that depend on the component type.</td></tr><tr><td><code>clearOnSubmit</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Clear on submit" property.</td></tr><tr><td><code>disableWhileSubmit</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Disable while submit" property.</td></tr><tr><td><code>getData(options)</code></td><td><code>(options: object) => object</code></td><td>Provides values of all direct and nested child inputs in one object, keyed by component name. The <code>options</code> determine the value formats. <a href="#getting-form-data">Learn more</a></td></tr><tr><td><code>headerText</code></td><td><code>string</code></td><td>Provides the current value of the "Header text" property.</td></tr><tr><td><code>hideButton</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Hide submit button" property.</td></tr><tr><td><code>reset()</code></td><td><code>() => void</code></td><td>Similar to <code>clear()</code>, however instead of making each child component empty, they are reset to their "default value". This allows to not clear the form entirely, but rather to reset it to the same default state that it had on initial app load.</td></tr><tr><td><code>showHeader</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Show header" property.</td></tr><tr><td><code>showLoadingWhileSubmit</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Show loading while submit" property.</td></tr><tr><td><code>setButtonColor(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the submit button color to the provided string value.</td></tr><tr><td><code>setButtonDisabled(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Button disabled" property to the provided boolean value.</td></tr><tr><td><code>setButtonFullWidth(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Full width" property to the provided boolean value.</td></tr><tr><td><code>setButtonText(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the submit button label to the provided string value.</td></tr><tr><td><code>setButtonTextColor(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the text color of the submit button to the provided string value.</td></tr><tr><td><code>setCancelButtonDisabled(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Cancel button disabled" property to the provided <em>boolean</em> value.</td></tr><tr><td><code>setCancelButtonText(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the "Cancel button text" property to the provided string value.</td></tr><tr><td><code>setClearOnSubmit(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Clear on submit" property to the provided <em>boolean</em> value.</td></tr><tr><td><code>setData(data)</code></td><td><code>(data: object) => void</code></td><td>Fills the form child inputs with the provided values. <a href="#setting-form-data">Learn more</a></td></tr><tr><td><code>setDisableWhileSubmit(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Disable while submit" property to the provided <em>boolean</em> value.</td></tr><tr><td><code>setHeaderText(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the "Header text" property to the provided <em>string</em> value. </td></tr><tr><td><code>setHideButton(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Hide submit button" property to the provided <em>boolean</em> value.</td></tr><tr><td><code>setShowHeader(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Show header" property to the provided <em>boolean</em> value.</td></tr><tr><td><code>setShowLoadingWhileSubmit(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Show loading while submit" property to the provided <em>boolean</em> value.</td></tr><tr><td><code>submit()</code></td><td><code>() => Promise&#x3C;void></code></td><td>Submits the form, i.e. applies exactly the same logic like when the user clicks on the submit button. This triggers a sequence of events, about which you can <a href="#submitting-a-form">read more here</a>.</td></tr></tbody></table>


# Funnel

The `Funnel` component allows visualizing of data in a funnel structure, for example, a conversion funnel on a website. The funnel can be configured to display horizontally (see below) or vertically.

<figure><img src="/files/ZABHPsUepX3bNMp4dJQ8" alt=""><figcaption><p>Funnel example</p></figcaption></figure>

### Data structure

The `Funnel` component can be used to display a single group of data, or multiple data series that flow through the same funnel steps. Both the data, as well as colors are configured via the "Data" configuration property.

It expects a specific JSON structure, which is defined as follows:

```typescript
interface FunnelData {
    colors: Array<string | string[]>;
    labels: string[];
    subLabels?: string[] | null;
    values: number[] | number[][];
}
```

This data structure covers multiple scenarios on how the `Funnel` can be used.

Let us firstly explore the different properties of the JSON structure, using the conversion funnel example above. The JSON data for such a funnel looks as follows:

```typescript
{
  "labels": ["Impressions", "Add To Cart", "Buy"],
  "subLabels": ["Direct", "Social Media", "Ads"],
  "colors": ["#6389E9", "#8DA7EE", "#B5C6F1"],
  "values": [
    [12000, 15000, 24000],
    [5700, 6400, 7800],
    [620, 750, 960]
  ]
}
```

In the example above, there are multiple "data series", that are differentiated by color. Furthermore, the user can explore the values of each series by hovering over the particular funnel step.

<figure><img src="/files/JVCdzGCvHh8RGVol78rF" alt=""><figcaption><p>Exploring series values by hover</p></figcaption></figure>

There are three funnel steps ("Impressions", "Add to Cart", "Buy") and there are three distinct data series for this funnel ("Direct", "Social Media", "Ads"). Each data series has a dedicated set of values and a color.&#x20;

* **labels**: This array defines the title of each funnel step. It is displayed at the top (horizontal direction) or on the left (vertical direction), where the data values and percentages are also visualized.
* **subLabels**: This array contains the titles of the data series, which are displayed on hover. This property is optional, i.e. nothing is rendered on hover in case it is omitted.
* **colors**: This array contains a color for each data series. The number of colors must always exactly match the number of data series, i.e. the length of the `subLabels` array (if provided), and the length of each array in `values`. The values in this array can be any valid CSS color, like for example `'red'`, `'#6389E9'` or `'rgba(0, 0, 127, 0.8)'`.
* **values**: Holds all values to be displayed in the funnel. In case there are multiple data series, this will be an array of arrays, while displaying a single set of values can be achieved with a flat array of numbers.

When not using multiple data series, the structure can look much simpler:

```typescript
{
  "labels": ["Impressions", "Add To Cart", "Buy"],
  "colors": ["#6389E9"],
  "values": [12000, 5700, 620],
}
```

This renders a simple funnel with only a single set of values:

<figure><img src="/files/xip62CTbb7O3avCq78Qn" alt=""><figcaption><p>Basic funnel with a single set of values</p></figcaption></figure>

### Gradients

You may have noticed that the `colors` property of the funnel data structure can be an "array of arrays of strings". The purpose of this is to provide the ability for color gradients within a data series. Change the example from the previous section as follows:

```typescript
{
  "labels": ["Impressions", "Add To Cart", "Buy"],
  "subLabels": ["Direct", "Social Media", "Ads"],
  "colors": [
    ["#FFB178", "#FF78B1", "#FF3C8E"],
    ["#A0BBFF", "#EC77FF"],
    "#A0F9FF"
  ],
  "values": [
    [12000, 15000, 24000],
    [5700, 6400, 7800],
    [620, 750, 960]
  ]
}
```

This renders the following funnel with color gradients:

<figure><img src="/files/Y4MGcphRodt9xuBwjEVW" alt=""><figcaption><p>Funnel with gradients</p></figcaption></figure>

When working with gradients in the `colors` property, consider the following:

* A gradient can consist of more than two colors. You can provide as many colors as you like, and the gradient will linearly transition through the provided colors in the provided order.
* You can freely mix gradients and fixed colors. In the example above, the first two data series are visualized with a gradient, while the third series uses the fixed color `"#A0F9FF"`

{% hint style="info" %}
In case of a single data series, there is no need to provide an array of arrays to the `colors` property, but it must be a flat array of strings. This means, that you don't provide something like `[["#A0BBFF", "#EC77FF"]]`, but instead: `["#A0BBFF", "#EC77FF"]`
{% endhint %}

### Properties

The `Funnel` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Data</td><td><code>object</code></td><td>Conversion funnel example, as shown above</td><td>Determines the values, labels and colors of the funnel displayed. <a href="#data-structure">Learn more</a></td></tr><tr><td>Direction</td><td><code>'horizontal' | 'vertical'</code></td><td><code>'horizontal'</code></td><td>Determines whether to render the funnel horizontally or vertically.</td></tr><tr><td>Display percent</td><td><code>boolean</code></td><td><code>true</code></td><td>Whether the percentages of each step should be displayed in the header or not. These percentages are always calculated against the value of the first step, i.e. against the "funnel entry". There is currently no way to change this logic.</td></tr></tbody></table>

### Events

The `Funnel` component does not offer any UI events.&#x20;

### API

The `Funnel` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>data</code></td><td><code>object</code></td><td>Provides the currently displayed funnel data. <a href="#data-structure">Learn more</a></td></tr><tr><td><code>displayPercent</code></td><td><code>boolean</code></td><td>Provides the current value of the "Display percent" property</td></tr><tr><td><code>direction</code></td><td><code>'horizontal' | 'vertical'</code></td><td>Provides the current value of the "Direction" property</td></tr><tr><td><code>setData(data)</code></td><td><code>(data: FunnelData) => void</code></td><td>Updates the funnel data. The provided object must match the <code>FunnelData</code> interface. <a href="#data-structure">Learn more</a></td></tr><tr><td><code>setDisplayPercent(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the value of the "Display percent" property to the provided boolean value.</td></tr><tr><td><code>setDirection(value)</code></td><td><code>(value: 'horizontal' | 'vertical') => void</code></td><td>Sets the value of the "Direction" property to the provided value. Throws an error in case the value is not either <code>'horizontal'</code> or <code>'vertical'</code>.</td></tr></tbody></table>


# Inputs

Input fields are essential building blocks of almost any application. Such fields target specific types of content, like simple text, numbers, passwords or dates. For this purpose, Uify offers many different input field components targeting those different content types.

Due to similarities in their UI/UX, those components have many configuration properties and API fields in common, but expose a few custom properties that are specific to their particular functionality. This page introduces all input components by describing the shared properties first, and then covering the specific properties of each component separately.

### Input types

The most basic input field is a simple **Text Input**. It allows to enter single-line text without any line breaks and is perfectly suited for short inputs like names, street names, etc.

<figure><img src="/files/AxNnCmwxKyi3RkIF2sRd" alt=""><figcaption><p>Text input</p></figcaption></figure>

In case single-line text is not suitable and a larger amount of text should be entered, the **Textarea** is a better fit. It allows to display more than one line of text and to enter line breaks as well:

<figure><img src="/files/avPtESctYsP2MkDl7f4f" alt=""><figcaption><p>Textarea</p></figcaption></figure>

A more specific type of inputs is the **Password Input**. It hides the input, unless the user deliberately decides to make the entered value visible by clicking on the icon on the right:

<figure><img src="/files/rYYksIgBnxeIgjdRdrmc" alt=""><figcaption><p>Password Input</p></figcaption></figure>

Another specialized input type is the **Number Input**. It accepts numeric input only, and can be configured to accept negative values and decimal values. The arrow buttons on the right allow incrementing or decrementing the input value by mouse click:

<figure><img src="/files/qOn5P731t8DQceu0KBv2" alt=""><figcaption><p>Number Input</p></figcaption></figure>

Another number-based input is the **Percent Input**. It offers the same UX and functionality of the Number Input, but auto-appends the percentage sign. The `value` of this input is exposed as decimal value, for example "25%" will be programmatically exposed as the decimal 0.25:

<figure><img src="/files/hSoiTyI1yS7SaBmtI6uV" alt=""><figcaption><p>Percent Input</p></figcaption></figure>

The **Email Input** and **URL Input** are standard text inputs, but validate the entered value for correctness:

<figure><img src="/files/Pec1j9wlqaQCur8go5Hn" alt=""><figcaption><p>Email and URL Input</p></figcaption></figure>

The **Slider Input** is an intuitive way to quickly set a value within a range with a certain step size without the need to type a numerical value:

<figure><img src="/files/OnUtKjOh7Gc5EvWenQKT" alt=""><figcaption><p>Slider Input</p></figcaption></figure>

The **File Input** allows users to upload a digital file from their device to be used by an appropriate endpoint:

<figure><img src="/files/K8QkdGISU6cZ3SrcjEWw" alt=""><figcaption></figcaption></figure>

Last but not least, the **Date Input**, **Time Input,** **Date & Time and Date Range Input** allow the user to enter temporal values. The format is fully configurable and convenient date- and time-pickers make it easy to find the right value. A manual entry and adjustment of the value is possible as well:

<figure><img src="/files/y0k94FgeOfdxW92JjOGO" alt=""><figcaption><p>Date, Time, Date &#x26; Time and Date Range Inputs</p></figcaption></figure>

In case the user enters a value that is not a valid date or time, according to the configured format, the value is set to the current date and/or time when the input loses focus. This guarantees that if the input contains a value, it will always be valid:

<figure><img src="/files/FePHv2jVPHP5EtGBvBJu" alt=""><figcaption><p>Handling invalid input</p></figcaption></figure>

### Label positioning

There are two options where the input label can be positioned: To the left of the input, or on top of it. This is automatically adjusted based on the size of the component. The label will be placed above the input as soon as there is enough space for it. Otherwise, the label is positioned to the left of the input in the same line. All inputs except the Textarea will never expand beyond a single line of input. Since the Textarea may contain multi-line content though, it will take the remaining vertical space of the component entirely:

<figure><img src="/files/VYZbIM9J0YBO8DwxgOdK" alt=""><figcaption><p>Label position is determined by component size automatically</p></figcaption></figure>

### Switching types

You may change the type of an existing input component by changing the "Input type" property in the properties panel. This will have the following effects on the configured properties of the input component:

1. Values of properties that both input types have in common (e.g. the "Disabled" property) will remain the same.
2. Values of properties that do not exist in the target input type (e.g. the "Allow decimals" property of a Numbers Input, which does not exist for the Text Input) will be discarded.
3. Existing validation rules will always be dropped entirely.
4. When switching to the Email Input or URL Input, the respective validation rules will be added.

{% hint style="info" %}
Due to effect 2. and 3., switching an input component to another type, and then back to the initial type, might not result in exactly the same property settings. Therefore, please be careful and aware of these effects when switching input types.
{% endhint %}

### Validation rules

Validating user input is a very common requirement in many applications. You may attach any of the predefined validation rules to input components, or you can create validation actions with custom logic yourself. These validation rules are managed in the properties panel. Some input types have validation rules pre-configured (e.g. the **Email Input** and the **URL Input**).

#### Validation trigger

One fundamental decision that you need to make: Should validation rules be applied on every key stroke (i.e. the user will receive immediate feedback while typing) or only on submit of a surrounding [Form](/component-reference/form) component? This is configured via the "Validate while typing" property. Especially in case the validation is complex, for example because it involves interactions with external data sources, it is highly recommended to disable this setting. In case however you would like to validate an input component outside of a Form context, this setting should be enabled because the validation will otherwise never happen.

<figure><img src="/files/zeDxThibDTmDboCLOJ9R" alt=""><figcaption><p>Impact of the "Validate while typing" property</p></figcaption></figure>

#### Rule execution

In case an input has multiple rules configured, all rules are executed in parallel, not sequentially. This means, that all rules will always be executed and evaluation is not eagerly aborted in case one rule has already failed. Due to this execution model, the error message presented to the user will always include all messages from all failing validations.

#### "Required" validation

The "Required" property is a special kind of validation, because it is not listed among the validation rules. When this setting is enabled, an input without value is considered invalid when validations are triggered. Being "without value" has a different meaning for different input types:

* **Text, Textarea, Email, URL, Password**: These inputs are considered to not have a value when the length of the value is 0.
* **Number, Percent**: When these inputs do not contain at least one digit, they are considered to not have a value.
* **Date, Time, Date & Time, Date Range**: These inputs do not contain a value in case the input is cleared entirely. Since these inputs are filled with the current date and time in case the user enters an invalid value, this state is fairly well-defined.

This validation is triggered according to the same logic like other validation rules: When "Validate while typing" is enabled, this condition is checked on every change of the input value, otherwise only when a surrounding [Form](/component-reference/form) is submitted.

#### Minimum length & Maximum length

These rules are available for all textual inputs: **Text**, **Textarea**, **Email**, **URL**, **Password**. They require the input value to have a minimum or maximum amount of characters to be valid. The constraint value is configured with a JavaScript input, i.e. the limit can contain dynamic code that will be evaluated before the validation rule is applied.

#### Minimum value & Maximum value

These rules are available for numeric inputs: **Number** and **Percent**. The limits are entered with a JavaScript input, i.e. it may be highly dynamic. In case of the percent input, the limit should be entered as a decimal value, e.g. a minimum or maximum of 25% should be entered as `0.25`, not as `25`.

#### Email

Available for **Text Input**, **Textarea** and **Email Input**. It requires the input value to be a valid email address. This validation rule is automatically added to instances of the **Email Input** component.

#### URL

Available for **Text Input**, **Textarea** and **URL Input**. This rule can be configured to require a specific protocol (for example, HTTP, HTTPS or FTP). If no protocol is configured, a URL without protocol prefix is accepted (e.g. `google.com`), otherwise the protocol prefix must be provided for the value to be accepted. In addition to this, a comma-separated list of accepted domains can be provided. In case the entered value is not among the configured domains, the value is considered to be invalid and a warning is presented to the user. The input for configuring these domains is a simple text input, but may contain dynamic JavaScript expressions with the `{{ ... }}` syntax.

This validation rule is automatically added to instances of the **URL Input** component.

#### Regular expression

Available for all textual inputs: **Text**, **Textarea**, **Email**, **URL**, **Password**. The input for the RegEx is a text input, which may contain dynamic JavaScript expressions with the `{{ ... }}` syntax. You may enter the pattern with or without `/`, but if you decide to include them, you need to include them both at the beginning and the end. You may add modifiers and the special indicators `^` and `$`, i.e. for example `/^\d+\sabc$/i` is a perfectly valid input.

#### Before, After, Before time, After time

Available for the textual inputs **Date**, **Time** and **Date\&Time**. These rules define that the entered date or time must be before or after the configured threshold. The threshold is entered with a JavaScript input, i.e. it is expected to always be a dynamic expression. It may evaluate to a JavaScript `Date`, like e.g. `new Date(2022, 11, 1)` or an ISO date string, like e.g. `'2022-12-01'`. The "Before time" and "After time" rules will also accept a time string in the format `HH:mm`, like e.g. `'14:33'`.

#### Custom action

This validation rule is available for all input types. The associated action will be executed every time the validation rules are evaluated ([see triggers](#validation-trigger)). The action execution will receive the current input value as [argument](/writing-code/actions/execution-model#action-arguments), so that it is easily available for applying validation logic. The return value of the action will be used as follows:

* A falsy return value means that the value is not valid. The default error message will be shown to the user. Since `undefined` is considered falsy as well, custom validation actions should always return a value explicitly.
* A return value of type `string` will be used as custom error message. In this case, the value will be considered invalid, and the returned string will be displayed to the user as error message. Please note that the empty string is falsy, and will hence mean an invalid value. The default error message will be shown to the user in this case.
* A truthy return value, except for a value of type `string`, means that the input value is valid.
* In case the action throws an error, the value is considered invalid and a default error message is shown accordingly.

### Properties

All input components share the following set of configuration properties:

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Disabled</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines whether the input is disabled or not. When disabled, the user cannot focus the input and cannot modify the value. Validation rules are however applied either way.</td></tr><tr><td>Input type</td><td><code>string</code></td><td>Depends on input type</td><td>This property is used to switch an input to another type. <a href="#switching-types">Learn more</a></td></tr><tr><td>Label</td><td><code>stringWithJs</code></td><td>Depends on input type</td><td>Text that is displayed as label of the input, either to its left or above. <a href="#label-positioning">Learn more</a></td></tr><tr><td>Label alignment</td><td><code>'left' | 'center' | 'right'</code></td><td><code>'left'</code></td><td>Text alignment of the label.</td></tr><tr><td>Required</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines whether the input is considered invalid without a value. Learn more about this special kind of validation rules <a href="#required-validation">here</a></td></tr><tr><td>Validate while typing</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether the validation rules should be evaluated on every change of the input value, or only on surrounding <a href="/pages/zKC0dhqXeNBI8qzeUEIb">Form</a> submit. <a href="#validation-trigger">Learn more</a></td></tr><tr><td>Validation rules</td><td>List</td><td>None, except for <strong>Email Input</strong> and <strong>URL Input</strong></td><td>Set of validation rules that are applied on the configured trigger. <a href="#validation-rules">Learn more</a></td></tr></tbody></table>

#### Custom properties of textual inputs

This includes the **Text Input**, **Textarea**, **Password Input**, **Email Input** and **URL Input**:

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Default value</td><td><code>stringWithJs</code></td><td><code>''</code></td><td>Initital value of the input on app load. Also, this value is set when the <code>reset()</code> API method of the input, or a surrounding <a href="/pages/zKC0dhqXeNBI8qzeUEIb">Form</a>, is triggered.</td></tr><tr><td>Placeholder</td><td><code>stringWithJs</code></td><td><code>'Enter value' | 'Enter password'</code></td><td>Text that is displayed in the input when no value has been entered yet.</td></tr><tr><td>Show clear icon</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines whether an "x" icon will be shown when a value has been entered. Clicking this icon will clear the input entirely. Does not apply to <strong>Password Input</strong> and <strong>Textarea</strong>.</td></tr></tbody></table>

#### Custom properties of numeric inputs

This includes the **Number Input** and the **Percent Input**:

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Allow decimals</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines whether the user may enter decimal values. The decimal separator is always the dot (<code>.</code>). This setting also influences how pasting works - in case this setting is set to <code>false</code>, the decimal point and potential decimals will be cut off when pasted.</td></tr><tr><td>Allow negative</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines whether the user may enter negative values, or decrement the value beyond zero with the button controls. Also, when enabled, the user may paste a numeric text that begins with a dash. Otherwise, the paste is ignored.</td></tr><tr><td>Default value</td><td><code>js</code></td><td><code>''</code></td><td>Initial value of the input on app load. Also, this value is set when the <code>reset()</code> API method of the input, or a surrounding <a href="/pages/zKC0dhqXeNBI8qzeUEIb">Form</a>, is triggered. This JavaScript expression is supposed to evaluate to a value of type <code>number</code>. Otherwise, it is ignored and the input is kept empty. In case the given value does not comply with the configured rules, e.g. the value is negative but the "Allow negative" property is set to <code>false</code>, the value is anyway used as input value.</td></tr><tr><td>Placeholder</td><td><code>stringWithJs</code></td><td><code>'Enter number'</code></td><td>Text that is displayed in the input when no value has been entered yet.</td></tr><tr><td>Show controls</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether the increment and decrement button will be displayed to the right of the input. Those controls allow to increment and decrement the value with mouse click.</td></tr></tbody></table>

#### Custom properties of date/time inputs

This includes the **Date Input**, **Time Input** and the **Date & Time Input**:

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Custom date format</td><td><code>stringWithJs</code></td><td><code>'yyyy-MM-dd'</code></td><td>Custom format to be used instead of the preconfigured options of the "Date format" property. Only available when the "Date format" property is set to "Custom". The format should be written as described for <a href="https://date-fns.org/v2.29.3/docs/format">date-fns.format</a>.</td></tr><tr><td>Custom time format</td><td><code>stringWithJs</code></td><td><code>'HH:mm'</code></td><td>Custom format to be used instead of the preconfigured options of the "Time format" property. Only available when the "Time format" property is set to "Custom". The format should be written as described for <a href="https://date-fns.org/v2.29.3/docs/format">date-fns.format</a>.</td></tr><tr><td>Date format</td><td><code>string</code></td><td><code>'yyyy-MM-dd'</code></td><td>Display format of the date part in the input, and which is used to parse the input value in case of manual input. This setting also influence the format of the   <code>valueFormattedString</code> API property.</td></tr><tr><td>Default value</td><td><code>js</code></td><td><code>null</code></td><td>Initial value of the input on app load. Also, this value is set when the <code>reset()</code> API method of the input, or a surrounding <a href="/pages/zKC0dhqXeNBI8qzeUEIb">Form</a>, is triggered. The JavaScript expression should either evaluate to a <code>Date</code> object, or an ISO 8601 date string. For a Time Input, a time string in the format of <code>HH:mm</code>, like e.g. <code>'14:30'</code>, is allowed as well.</td></tr><tr><td>First day of week</td><td><code>'Sunday' | 'Monday'</code></td><td><code>'Sunday'</code></td><td>This setting influences the first day of the week in the date picker.</td></tr><tr><td>Show clear icon</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines whether an "x" icon will be shown when a value has been entered. Clicking this icon will clear the input entirely.</td></tr><tr><td>Time format</td><td><code>string</code></td><td><code>'HH:mm'</code></td><td>Display format of the time part of the input value, and format that is used to parse the input value in case of manual input. This setting also influence the format of the   <code>valueFormattedString</code> API property.</td></tr></tbody></table>

**Custom inputs of date range**

<table><thead><tr><th width="157">Property</th><th width="159">Type</th><th width="167">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Default start date</td><td><code>js</code></td><td><code>null</code></td><td>Initial value of the input on app load. Also, this value is set when the <code>reset()</code> API method of the input, or a surrounding <a href="/pages/zKC0dhqXeNBI8qzeUEIb">Form</a>, is triggered. The JavaScript expression should either evaluate to a <code>Date</code> object, or an ISO 8601 date string. </td></tr><tr><td>Default end date</td><td><code>js</code></td><td><code>null</code></td><td>Initial value of the input on app load. Also, this value is set when the <code>reset()</code> API method of the input, or a surrounding <a href="/pages/zKC0dhqXeNBI8qzeUEIb">Form</a>, is triggered. The JavaScript expression should either evaluate to a <code>Date</code> object, or an ISO 8601 date string. </td></tr><tr><td>Placeholder start date</td><td><code>js</code></td><td><code>null</code></td><td></td></tr><tr><td>Placeholder end date</td><td><code>js</code></td><td><code>null</code></td><td></td></tr><tr><td>Date format</td><td><code>string</code></td><td><code>'yyyy-MM-dd'</code></td><td>Display format of the date part in the input, and which is used to parse the input value in case of manual input. This setting also influence the format of the   <code>valueFormattedString</code> API property.</td></tr><tr><td>First day of week</td><td><code>'Sunday' | 'Monday'</code></td><td><code>'Sunday'</code></td><td>This setting influences the first day of the week in the date picker.</td></tr><tr><td>Show clear icon</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines whether an "x" icon will be shown when a value has been entered. Clicking this icon will clear the input entirely.</td></tr></tbody></table>

**Custom properties of slider inputs**

<table><thead><tr><th width="157">Property</th><th width="159">Type</th><th width="172">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Default value</td><td><code>js</code></td><td>1</td><td>Initial value of the input on app load. Also, this value is set when the <code>reset()</code> API method of the input, or a surrounding <a href="/pages/zKC0dhqXeNBI8qzeUEIb">Form</a>, is triggered. The JavaScript expression should evaluate to a <code>Number</code>.</td></tr><tr><td>Minimum value</td><td><code>js</code></td><td>1</td><td>The JavaScript expression should evaluate to a <code>Number</code>.</td></tr><tr><td>Maximum value</td><td><code>js</code></td><td>5</td><td>The JavaScript expression should evaluate to a <code>Number</code>. Note that the maximum may never be reached if it does not equal the sum of the minimum value and a multiple of the step size.</td></tr><tr><td>Step size</td><td><code>js</code></td><td>1</td><td>The JavaScript expression should evaluate to a <code>Number</code>. The step size describes in which increments the value of the slider increases when the slider is dragged.</td></tr></tbody></table>

#### Custom properties of file input

<table><thead><tr><th width="157">Property</th><th width="159">Type</th><th width="172">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Allowed file types</td><td><code>js</code></td><td><code>null</code></td><td>If you want to restrict the types of files that a user can upload, you simply provide an array of file extensions to whitelist them. If it is empty, it allows all file types.</td></tr><tr><td>Allow multiple files</td><td><code>boolean</code></td><td><code>false</code></td><td>Defines if the user can select and upload multiple files.</td></tr></tbody></table>

### Events

All input components offer the same set of UI events:

* **On change**: This event is triggered whenever the input value changes, e.g. because the user is typing, has pasted new content or has cleared the input by clicking on the clear-icon. This event is not triggered when the value changes programmatically via the `setValue`, `clear` or `reset` methods.&#x20;
* **On blur**: This event is triggered when the input loses focus.

### API

All input components share the following properties and methods in their JavaScript API:

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>clear()</code></td><td><code>() => void</code></td><td>Clears the input, i.e. sets the input for the empty string for textual input types, and to <code>null</code> for all others.</td></tr><tr><td><code>disabled</code></td><td><code>boolean</code></td><td>Current value of the "Disabled" property.</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>Current value of the "Label" property.</td></tr><tr><td><code>reset()</code></td><td><code>() => void</code></td><td>Sets the value of the input to the current value of the "Default value" property. This is useful when a form or individual component should not just be emptied, but instead reset to what it was initially on app load.</td></tr><tr><td><code>setDisabled(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Disabled" property to the provided value.</td></tr><tr><td><code>setLabel(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the "Label" property to the provided value.</td></tr><tr><td><code>validate()</code></td><td><code>() ⇒ Promise</code></td><td>We return a <code>Promise</code> that runs all validation rules for the current value. When the promise resolves, a <code>boolean</code> is returned. If <code>true</code> is returned, the input is valid, otherwise it is invalid. </td></tr></tbody></table>

#### API of textual inputs

This includes the **Text Input**, **Textarea**, **Password Input**, **Email Input** and **URL Input**:

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>passwordVisibility</code></td><td><code>boolean</code></td><td><code>true</code> in case the input value is currently visible as clear text, i.e. the user clicked the visibility icon, <code>false</code> otherwise. Only for <strong>Password Input</strong>.</td></tr><tr><td><code>placeholder</code></td><td><code>string</code></td><td>Current value of the "Placeholder" property.</td></tr><tr><td><code>setPasswordVisibility(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the password visibility, i.e. the password is displayed as clear text when true is provided as argument, otherwise the password is hidden. Only for <strong>Password Input</strong>.</td></tr><tr><td><code>setPlaceholder(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the "Placeholder" property to the provided value.</td></tr><tr><td><code>setValue(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the input value to the provided value.</td></tr><tr><td><code>value</code></td><td><code>string</code></td><td>Current value of the input.</td></tr></tbody></table>

#### API of numeric inputs

This includes the **Number Input** and the **Percent Input**:

<table><thead><tr><th width="277">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>allowDecimals</code></td><td><code>boolean</code></td><td>Current value of the "Allow decimals" property.</td></tr><tr><td><code>allowNegative</code></td><td><code>boolean</code></td><td>Current value of the "Allow negative" property.</td></tr><tr><td><code>placeholder</code></td><td><code>string</code></td><td>Current value of the "Placeholder" property.</td></tr><tr><td><code>setAllowDecimals(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Allow decimals" property to the provided value.</td></tr><tr><td><code>setAllowNegative(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Allow negative" property to the provided value.</td></tr><tr><td><code>setPlaceholder(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the "Placeholder" property to the provided value.</td></tr><tr><td><code>setValue(value)</code></td><td><code>(value: number | null) => void</code></td><td>Sets the input value to the provided value. In case <code>null</code> is provided, the input is cleared.  In case a negative value is provided, but the "Allow negative" property is currently set to <code>false</code>, the input value is set to <code>0</code>. In case a decimal value is provided, but the "Allow decimals" property is set to <code>false</code>, the decimals are cut off.</td></tr><tr><td><code>value</code></td><td><code>number | null</code></td><td>Provides the current value of the input. <code>null</code> in case no value has been entered, otherwise a <code>number</code>.</td></tr></tbody></table>

#### API of date/time inputs

This includes the **Date Input**, **Time Input,** **Date & Time Input** and **Date Range Input**:

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>setValue(value)</code></td><td><code>(value: string | Date | null) => void</code> (except date range input: <code>(value: {state: string | Date | null, end: string | Date | null}</code>)</td><td>Sets the input value to the provided value. In case <code>null</code> is provided, the input is cleared.  In case a <code>Date</code> object is provided, the date and time parts are filled according to its values. In case of an argument of type <code>string</code>, the string is expected to either be an ISO 8601 string, or a string that is formatted according to the "Date format" and "Time format" (or, "Custom date format" / "Custom time format") properties. Otherwise, an error is thrown.</td></tr><tr><td><code>value</code></td><td><code>Date | null</code> (excpet date range input: <code>{state: Date | null, end: Date | null}</code>)</td><td>Provides the current value of the input. <code>null</code> in case no value has been entered, otherwise a <code>Date</code> object. In case of a <strong>Date Input</strong>, the time part of the <code>Date</code> will be 00:00:00.</td></tr><tr><td><code>valueFormattedString</code></td><td><code>string | null</code></td><td>Provides the current value of the input as a formatted string, using the formats from the "Date format" / "Custom date format" / "Time format" / "Custom time format" properties. <code>null</code> in case the input currently has no value. Date and time parts are separated by a space (<code>' '</code>).</td></tr><tr><td><code>valueISOString</code></td><td><code>string | null</code></td><td>Provides the current input value as a string in ISO 8601 format. <code>null</code> in case the input currently has no value. For a <strong>Time input</strong>, the value is formatted as <code>HH:mm</code>. The <strong>Date Input</strong> uses <code>yyyy-MM-dd</code>. </td></tr></tbody></table>

**API of the slider**

<table><thead><tr><th width="271.3333333333333">Property / Function</th><th width="215">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>maximum</code></td><td><code>number</code></td><td>Current value of "Maximum" property.</td></tr><tr><td><code>setMaximum(value)</code></td><td><code>(value: number) => void</code></td><td>Sets the "Maximum" property to the provided value.</td></tr><tr><td><code>minimum</code></td><td><code>number</code></td><td>Current value of "Minimum" property.</td></tr><tr><td><code>setMinimum(value)</code></td><td><code>(value: number) => void</code></td><td>Sets the "Minimum" property to the provided value.</td></tr><tr><td><code>stepSize</code></td><td><code>number</code></td><td>Current value of "Step Size" property.</td></tr><tr><td><code>setStepSize(value)</code></td><td><code>(value: number) => void</code></td><td>Sets the "Step Size" property to the provided value.</td></tr><tr><td><code>value</code></td><td><code>number</code></td><td>Current value of the input.</td></tr><tr><td><code>setValue(value)</code></td><td><code>(value: number) => void</code></td><td>Sets the value to the provided value.</td></tr></tbody></table>

#### API of the file input

| Property / Function | Type                                           | Behavior                                                  |
| ------------------- | ---------------------------------------------- | --------------------------------------------------------- |
| files               | `{name: string, size: number, type: string}[]` | The file names, their sizes and types that were uploaded. |
| multiple            | `boolean`                                      | Returns if the file input allows multiple files.          |
| setMultiple(value)  | `(value: boolean) => void`                     | Set the "Allow multiple files" property.                  |
| accept              | `string[]`                                     | Returns a list of file extensions that are allowed.       |
| setAccept(value)    | `(value: string[]) => void`                    | Set the file extensions that are allowed to be uploaded.  |


# Modal

The `Modal` is the right choice for any kind of popup or dialog that you would like to show prominently on top of the regular app UI. Like the [Container](/component-reference/container), [Form](/component-reference/form) and [Slideout](/component-reference/slideout)components, the `Modal` acts as a host of child components with its own grid system. Due to its fixed position on the screen, you cannot change the component position or its dimensions by dragging and dropping.

<figure><img src="/files/QWQZSfd5hOvbDNyTLOky" alt=""><figcaption><p>The modal always keeps its fixed position</p></figcaption></figure>

### Accessing a modal

Since the `Modal` is usually not visible on the canvas, you might wonder how to access a closed modal in the app editor. You can use the *explorer* tab of the left-hand pane to open any modal component with a click.

<figure><img src="/files/n7kDWMt2B3MZwkYJDcSj" alt=""><figcaption><p>Accessing a closed modal in the editor</p></figcaption></figure>

{% hint style="info" %}
It is not possible to drag another component from outside a modal into a modal. Until this issue is resolved, we recommend using copy-paste to achieve this result:\
\
1\. Select the component(s) that you would like to move to a modal\
2\. Use CMD+c (Mac) / CTRL+c (Win) to copy the selected component(s)\
3\. Open the target modal via the explorer tab and make sure it is selected\
4\. Use CMD+v (Mac) / CTRL+v (Win) to paste the copied component(s) into the modal\
5\. Delete the copied component(s) outside the modal
{% endhint %}

### Modal sizing

Instead of resizing a `Modal` in the usual way with drag-and-drop, the `Modal` dimensions are determined by its "Width" and "Height" properties. You can place any valid CSS expression, for example, `80%`, `500px`, `80vh` or even `calc(100% - 50px)`, and these dimensions are applied to the `Modal`.

The `Modal` has a minimum height of `280px` and minimum width of `360px` to maintain basic usability that cannot be changed.

{% hint style="warning" %}
We recommend using relative sizing as much as possible. Absolute dimensions in pixels might lead to undesired overflow on small screens, or weirdly small modals on large screens.
{% endhint %}

### Properties

The `Modal` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="158">Type</th><th width="139">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Blurred background</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether a dark blurred background should cover the UI.</td></tr><tr><td>Close on escape</td><td><code>boolean</code></td><td><code>true</code></td><td>When set to <code>true</code>, an open modal closes when the user hits the escape key. Otherwise, this keypress is ignored.</td></tr><tr><td>Close on outside click</td><td><code>boolean</code></td><td><code>true</code></td><td>When set to <code>true</code>, an open modal closes when the user clicks outside its content, i.e. on the blurred background. It is recommended to disable this for complex modals, of which the user would not like to lose state easily.</td></tr><tr><td>Header text</td><td><code>stringWithJs</code></td><td><code>'Header'</code></td><td>The text to be displayed in the header. Only available when the "Show header" property is set to <code>true.</code></td></tr><tr><td>Height</td><td><code>string</code></td><td><code>'60vh'</code></td><td>CSS height of the modal. See considerations <a href="#undefined">above</a>.</td></tr><tr><td>Scrollable</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether the modal content is scrollable or not. When set to <code>false</code>, overflowing content is not be accessible for the end user.</td></tr><tr><td>Show close icon</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether there should be the "x" icon in the top-right corner for closing the modal. In case "Show header" is set to <code>false</code>, the icon might overlap with the modal content.</td></tr><tr><td>Show header</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether the modal should have a header. When set to <code>true</code>, the header text can be configured via the "Header text" property.</td></tr><tr><td>Visible</td><td><code>boolean</code></td><td><code>false</code></td><td>The "Visible" property behaves differently than in other components. For the <code>Modal</code>, this property determines whether the modal is open or closed. You might apply a dynamic JavaScript expression here, but usually a modal should be opened or closed explicitly using the exposed functions.</td></tr><tr><td>Width</td><td><code>string</code></td><td><code>'60vw'</code></td><td>CSS width of the modal. See considerations <a href="#undefined">above</a>.</td></tr></tbody></table>

Review the notes regarding the "Scrollable" property in the [Container](/component-reference/container) reference, the `Modal` behaves in exactly the same way.

{% hint style="info" %}
An open modal is closed automatically when another modal or slideout is opened. This means, that you do not need to close a slideout explicitly when opening another modal or a slideout.
{% endhint %}

### Events

The `Modal` offers two events to associate actions with:

* **On show**: This action is executed whenever the modal is opened, regardless of the reason ("Visible" property, `show()` function, selecting the component in the explorer tab of the app editor).
* **On hide**: This action is executed when a modal is closed, regardless of the reason.

To maintain a pleasant user experience, the "On show" action is executed asynchronously with displaying the modal. A long-running action does not block the modal render, but leads to a spinner until the action run has finished. This is useful, for example, fetching data from sources when a modal is opened.

<figure><img src="/files/DCBIApvkvCssWhsw35sg" alt=""><figcaption><p>Asynchronous execution of "On show" action</p></figcaption></figure>

The "On hide" action is executed asynchronously as well, i.e. the modal will be closed instantly, but the action might still be running. Again, this leads to a more snappy UI and better user experience. There is currently no way to modify this action execution behavior.

### API

The `Modal` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>blurredBackground</code></td><td><code>boolean</code></td><td>Current boolean value of the "Blurred background" property.</td></tr><tr><td><code>close()</code></td><td><code>() => void</code></td><td>Closes the modal and executes the "On hide" action <a href="#events">asynchronously</a>. Noop in case the modal is not open.</td></tr><tr><td><code>closeOnEscape</code></td><td><code>boolean</code></td><td>Current boolean value of the "Close on escape" property.</td></tr><tr><td><code>closeOnOutsideClick</code></td><td><code>boolean</code></td><td>Current boolean value of the "Close on outside click" property.</td></tr><tr><td><code>headerText</code></td><td><code>string</code></td><td>Current string value of the "Header text" configuration.</td></tr><tr><td><code>height</code></td><td><code>string</code></td><td>Current css-string value of the "Height" property.</td></tr><tr><td><code>hide()</code></td><td><code>() => void</code></td><td>Alias for <code>close()</code>.</td></tr><tr><td><code>open()</code></td><td><code>() => void</code></td><td>Alias for <code>show()</code>.</td></tr><tr><td><code>setBlurredBackground(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Blurred background" property to the provided boolean value.</td></tr><tr><td><code>setCloseOnEscape(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Close on escape" property to the provided boolean value.</td></tr><tr><td><code>setCloseOnOutsideClick(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Close on outside click" property to the provided boolean value.</td></tr><tr><td><code>setHeaderText(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the header text to the provided string value.</td></tr><tr><td><code>setHeight(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the "Height" property to the provided string value. The value should be a valid CSS expression. This is not validated.</td></tr><tr><td><code>setShowHeader(value)</code></td><td><code>(value: boolean) => void</code></td><td>Adjusts the flag whether the header text should be displayed to the provided boolean value.</td></tr><tr><td><code>setWidth(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the "Width" property to the provided string value. The value should be a valid CSS expression. This is not validated.</td></tr><tr><td><code>show()</code></td><td><code>() => void</code></td><td>Opens the modal and executes the "On show" action <a href="#events">asynchronously</a>. Closes any other modal that is currently open. Noop in case the modal is already open.</td></tr><tr><td><code>showHeader</code></td><td><code>boolean</code></td><td>Current boolean value of the "Show header" configuration.</td></tr><tr><td><code>width</code></td><td><code>string</code></td><td>Current css-string value of the "Width" property.</td></tr></tbody></table>


# PDF

The `PDF` component is a simple pdf reader that allows you to display uploaded supplier documents, your candidate's CV or product specifications. The only thing it requires is a url pointing to the pdf document.

### Properties

The `Modal` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="158">Type</th><th width="139">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Page scale</td><td><code>'width' | 'height'</code></td><td><code>width</code></td><td>Determines if the component scales the pdf to its width or heigth, i.e. if the full width or height of the component should be visible.</td></tr><tr><td>Show footer</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines if the footer should be visible. If the footer is shown, the pdf is paginated, i.e. the user can navigate to a different page of the pdf with the errors in the footer. If the footer is hidden, the pages are reachable by scroll. Additionally, a download button is available in the footer.</td></tr><tr><td>URL</td><td><code>stringWithJs</code></td><td><pre data-overflow="wrap"><code>'https://assets-staging.uify.io/pdf/sample.pdf'
</code></pre></td><td>Defines the path of the url to be displayed in the component. By using <code>{{ }}</code>, the url can be set programmatically e.g. through a select.</td></tr></tbody></table>

### Events

`PDF` does not offer any events.

### API

The `PDF` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>backgroundColor</code></td><td><code>string</code></td><td>Current color of the brackground if any</td></tr><tr><td><code>borderColor</code></td><td><code>string</code></td><td>Current color of the border if any</td></tr><tr><td><code>pageScale</code></td><td>"<code>width" | "height"</code></td><td>Whether the dimensions scale to the width or the height of the pdf</td></tr><tr><td><code>setBackgroundColor(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the background color</td></tr><tr><td><code>setBorderColor(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the border color</td></tr><tr><td><code>setPageScale(value)</code></td><td><code>(value: "width" | "height") => void</code></td><td>Sets the page scale to either <code>width</code> or <code>height</code></td></tr><tr><td><code>setShowFooter(value)</code></td><td><code>(value: boolean) => void</code></td><td>Defines if the footer should be displayed</td></tr><tr><td><code>setUrl(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the url of the pdf</td></tr><tr><td><code>showFooter</code></td><td><code>boolean</code></td><td>Whether the footer is visible or not</td></tr><tr><td><code>url</code></td><td><code>string</code></td><td>Url of the pdf document</td></tr></tbody></table>


# Radio group

The `Radio group` component allows for the selection of a single value from a list of options. The options are presented as [radio buttons](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio), which have a value and a label. Like inputs, [switches](/component-reference/switch) and selects, the `Radio group` fits well into the context of a `Form`.

<figure><img src="/files/zFbixd2tDKpRzKxpKPJL" alt=""><figcaption><p>Radio group component</p></figcaption></figure>

### Properties

The `Radio group` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Default value</td><td><code>string</code></td><td><code>'one'</code></td><td>Determines which radio button will be selected by default at first render. This value is also used when the <code>reset()</code> function is called.</td></tr><tr><td>Disabled</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether the input should be disabled or not. When disabled, the input is greyed out and values cannot be toggled.</td></tr><tr><td>Label position</td><td><code>'left' | 'right'</code></td><td><code>'left'</code></td><td>Determines whether the labels of the radio buttons are on the left or the right.</td></tr><tr><td>Labels</td><td><code>array</code></td><td><code>['One', 'Two', 'Three']</code></td><td>Labels to be rendered for each option. In case there are more "Labels" than "Values", the redundant labels are ignored. If there are more "Values" than "Labels", the additional values are rendered with their raw value.</td></tr><tr><td>Text alignment</td><td><code>'left' | 'center' | 'right'</code></td><td><code>'left'</code></td><td>Alignment of the radio button labels and the "Title" text. There is no way to set these alignments separately.</td></tr><tr><td>Title</td><td><code>stringWithJs</code></td><td><code>'Radio group'</code></td><td>The text rendered above the radio button inputs. Does not create unnecessary whitespace when left empty.</td></tr><tr><td>Toggle with label</td><td><code>boolean</code></td><td><code>false</code></td><td>By default, the radio buttons can only be selected when clicking directly on the input. When this property is set to <code>true</code>, a click on the label text will change selection as well.</td></tr><tr><td>Values</td><td><code>array</code></td><td><code>['one', 'two', 'three']</code></td><td>Array of internal values of the options. Its length determines how many radio options are rendered. Non-string elements of the array are ignored.</td></tr></tbody></table>

### Events

The `Radio group` offers only a single event **"On change"**. This event is triggered whenever the selected option is changed by the user. Programmatically changing the value by using the `reset()` or `setValue(value)` functions does not trigger this event.

### API

The `Radio group` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td> <code>disabled</code></td><td><code>boolean</code></td><td>Current boolean value of the "Disabled" property.</td></tr><tr><td><code>labels</code></td><td><code>string[]</code></td><td>Provides the current value of the "Labels" property. If the number of values and labels is not equal, the unpruned array will be returned, even if not all labels are used.</td></tr><tr><td><code>reset()</code></td><td><code>() => void</code></td><td>Resets the radio group similar to the current value of the "Default value" property. This is especially useful when a <code>Form</code> is reset to its default state.</td></tr><tr><td><code>setDisabled(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the value of the "Disabled" property to the provided boolean value.</td></tr><tr><td><code>setLabels(value)</code></td><td><code>(value: string[]) => void</code></td><td>Sets the value of the "Labels" property to the provided strings array.</td></tr><tr><td><code>setTitle(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the "Title" property to the provided string value.</td></tr><tr><td><code>setValues(value)</code></td><td><code>(value: string[]) => void</code></td><td>Sets the option values to the provided string array.</td></tr><tr><td><code>title</code></td><td><code>string</code></td><td>Provides the current string value of the "Text" property.</td></tr><tr><td><code>value</code></td><td><code>string</code></td><td>Value of the currently selected radio button. Should always be a member of the array returned by <code>values</code></td></tr><tr><td><code>values</code></td><td><code>string[]</code></td><td>Current value of the "Values" property.</td></tr></tbody></table>


# Selects

The `Select` and `Multiselect` components offer the user a choice from a configurable set of options in a dropdown. Depending on the type, either exactly one or any amount of options can be selected. A textual search supports the user with finding the right options.

<figure><img src="/files/nZkwc67O3oKhKZAIarPV" alt=""><figcaption><p>Select component with applied search</p></figcaption></figure>

### Select vs. Multiselect

The `Select` and `Multiselect` differ only in one key aspect. The `Select` allows the selection of only a single option at most, while the `Multiselect` allows the selection of as many options as the user likes. Due to this, selected values are visualized differently.

<figure><img src="/files/QPa8KPx2CShawX2rN5g2" alt=""><figcaption><p>Selected options in Select vs. Multiselect</p></figcaption></figure>

The `Multiselect` renders all selected options and allows to remove them with one click on the "x" icon. For a convenient selection and deselection of multiple options at once, the `Multiselect` keeps the dropdown open while selections are made. The dropdown of the `Select` is closed immediately after an option was selected or deselected.

<figure><img src="/files/OPB4J7Rsp6MxdgrlJ8ji" alt=""><figcaption><p>UX of selection and deselection</p></figcaption></figure>

The "Select type" property turns a `Select` component into a `Multiselect` and vice versa. In case of a `Multiselect` with multiple selected options being turned into a `Select`, only the first selected option is kept.

<figure><img src="/files/AmYKZZt930iLoVgpwn4A" alt=""><figcaption><p>Turning a Multiselect into a Select</p></figcaption></figure>

### Values and labels

Like the native HTML `<select>`, the values and rendered labels of the options may be different. In the country select examples above, ISO country codes like `AU` are used as values, while the visualized labels are the full country names like `Austria`. These values and labels are configured separately in the properties panel with the "Values" and "Labels" properties. Internally, these properties must have the following structure:

```typescript
interface SelectProperties {
  ...
  values: Array<string | number>;
  labels: string[] | Record<string, string>;
}
```

The values are a list of all values offered in the dropdown. The options are sorted in the same order as the values in this array. The labels can be an array of strings, and are then matched with the value in the same index. If the labels are provided as a string-to-string mapping, values are matched to labels by looking up the value in that dictionary:

```typescript
// Values are always an array:
values = [
  'AU',
  'BE',
  'DE',
  'UK',
];

// Option 1: Define labels by providing a string-array of same length
labels = [
  'Austria',
  'Belgium',
  'Germany',
  'United Kingdom',
];

// Option 2: Define labels as a string-to-string dictionary
labels = {
  AU: 'Austria',
  BE: 'Belgium',
  DE: 'Germany',
  UK: 'United Kingdom',
}
```

{% hint style="info" %}
Option 2 is useful when the values are dynamic. In this case, you can provide the entire dictionary of possible values, regardless of which values are currently offered. Otherwise, you need to make sure that labels are always filtered and sorted in the same way as the values.
{% endhint %}

The matching of labels to values is performed according to the following rules:

* `null` and `undefined` in values are ignored, i.e. they are removed before matching to labels.
* If labels are not provided at all, or do not evaluate to either an array or a dictionary, values are used as labels directly.
* If there are less values than labels, the additional labels are ignored.
* If there are more values than labels, the values without labels at their matching index are used as labels directly.
* If labels are provided as dictionary, but a value does not occur as key, that value is used as label directly.
  * Values of type `number` used as labels are stringified for the key.

### Properties

The `Select` components offer the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="166">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Alignment</td><td><code>'left' | 'center' | 'right'</code></td><td><code>'left'</code></td><td>Alignment of the label text next to the select.</td></tr><tr><td>Allow empty</td><td><code>boolean</code></td><td><code>true</code></td><td>Whether the select may have no option selected or not. When set to <code>false</code>, the user will not be able to deselect the last remaining option. It is  still possible in this case to clear the select programmatically, and to initialize it without any option in the "Default values" property.</td></tr><tr><td>Allow search</td><td><code>boolean</code></td><td><code>true</code></td><td>When set to <code>true</code>, the select offers to search for options.</td></tr><tr><td>Default values</td><td><code>Array&#x3C;string | number></code></td><td><code>[]</code></td><td>Values of the options that should be selected after initial app load. When the <code>reset()</code> API method is called, the selection is reset to the current values of this property. When used with a dynamic JavaScript expression, a change of dependencies updates the selection reactively (learn more about reactivity).</td></tr><tr><td>Disabled</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether the select should be disabled. When disabled, the component is greyed out and the dropdown cannot be opened.</td></tr><tr><td>Label</td><td><code>stringWithJs</code></td><td><code>'Select'</code></td><td>Text label shown next to the select.</td></tr><tr><td>Labels</td><td><code>string[] | Record&#x3C;string, string></code></td><td><code>['First', 'Second', 'Third']</code></td><td>Rendered option labels. <a href="#values-and-labels">Learn more</a></td></tr><tr><td>Placeholder</td><td><code>stringWithJs</code></td><td><code>'Select value'</code></td><td>Placeholder shown in the select when no value has been selected yet.</td></tr><tr><td>Required</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether at least one value must be selected when a surrounding form is submitted. Otherwise, form submission is aborted. <a href="/pages/zKC0dhqXeNBI8qzeUEIb#submitting-a-form">Learn more about form submission</a></td></tr><tr><td>Select type</td><td><code>'Select' | 'Multiselect'</code></td><td>Depends on chosen component type</td><td>Changes the component type to the chosen one. <a href="#select-vs.-multiselect">Learn more</a></td></tr><tr><td>Values</td><td><code>Array&#x3C;string | number></code></td><td>Numbers from 1 to 10</td><td>Option values. <a href="#values-and-labels">Learn more</a></td></tr></tbody></table>

### Events

The `Select` components offer three UI events:

* **On change run**: Action is run whenever the user changes selection. It is not executed when selection is changed programmatically in any way.
* **On focus run**: Action is run when the select gains focus.
* **On blur run**: Action is run when the select loses focus.

None of the associated actions receive any [arguments](/writing-code/actions/execution-model#action-arguments) when triggered.

### API

The `Select` components expose the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>clear()</code></td><td><code>() => void</code></td><td>Deselects all options.</td></tr><tr><td><code>disabled</code></td><td><code>boolean</code></td><td>Provides the current boolean value of the "Disabled" property.</td></tr><tr><td><code>labels</code></td><td><code>unknown</code></td><td>Provides the current value of the "Labels" property. No validation is applied, i.e. you receive the raw value, even if it is neither an array nor a dictionary (<a href="#values-and-labels">learn more</a>).</td></tr><tr><td><code>reset()</code></td><td><code>() => void</code></td><td>Sets the selected options to the current value of the "Default values" property.</td></tr><tr><td><code>setDisabled(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the value of the "Disabled" property to the provided boolean value.</td></tr><tr><td><code>setValues(values)</code></td><td><code>(values: Array&#x3C;string | number | null | undefined> | null) => void</code></td><td>Sets the selected values to the provided array. Values are stored even if they do not appear in the "values" property. This means, that a select may have entries in its selected values which may not be rendered. The select is cleared when <code>null</code> is provided.</td></tr><tr><td><code>setValue(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the selected value to the provided one. Like with <code>setValues</code>, it is not checked whether the provided value appears in the "values" property. The select is cleared when <code>null</code> is provided.</td></tr><tr><td><code>value</code></td><td><code>string | number</code></td><td>Provides the selected value, or <code>null</code> in case none is selected. In case multiple options are selected, only the first one is returned.</td></tr><tr><td><code>values</code></td><td><code>Array&#x3C;string | number></code></td><td>Provides the currently selected values as array. Empty array in case nothing is selected.</td></tr></tbody></table>


# Slideout

The `Slideout` component, also known as sliding panel or drawer, allows you to provide additional information or options for a record or specific context. It slides in and out of the view horizontally and covers a fraction of the main UI. Similar to the [Modal](/component-reference/modal), [Container](/component-reference/container) and [Form](/component-reference/form) it is a host. I.e. it operates as a canvas for all other components with its own grid system, such that you can customize the UI of the slideout to your needs by dragging and dropping components in it.

<figure><img src="/files/ZgM2dXNkf5b6OMKufSGF" alt=""><figcaption><p>Placing a slideout in your app</p></figcaption></figure>

### Accessing a slideout

Since the `Slideout` is usually not visible on the canvas, you might wonder how to access a closed slideout in the app editor. You can use the *explorer* tab of the left-hand pane to open any slideout component with a click.

<figure><img src="/files/8IBbXJxEpEFotIrHmwsX" alt=""><figcaption><p>Accessing a closed slideout in the editor</p></figcaption></figure>

{% hint style="info" %}
It is not possible to drag another component from outside a slideout into a slideout. Until this issue is resolved, we recommend using copy-paste to achieve this result:\
\
1\. Select the component(s) that you would like to move to a slideout\
2\. Use CMD+c (Mac) / CTRL+c (Win) to copy the selected component(s)\
3\. Open the target slideout via the explorer tab and make sure it is selected\
4\. Use CMD+v (Mac) / CTRL+v (Win) to paste the copied component(s) into the slideout\
5\. Delete the copied component(s) outside the slideout
{% endhint %}

### Slideout sizing

Instead of resizing a `Slideout` in the usual way with drag-and-drop, the `Slideout` dimension is determined by its "Width" property. You can place any valid CSS expression, for example, `80%`, `500px`, `80vh` or even `calc(100% - 50px)`, and these dimensions are applied to the `Slideout`.

The `Slideout` has a minimum height of `280px` and minimum width of `360px` to maintain basic usability that cannot be changed.

{% hint style="warning" %}
We recommend using relative sizing as much as possible. Absolute dimensions in pixels might lead to undesired overflow on small screens, or weirdly small slideout on large screens.
{% endhint %}

### Properties

The `Slideout` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="158">Type</th><th width="139">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Blurred background</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether a dark blurred background should cover the UI.</td></tr><tr><td>Close on escape</td><td><code>boolean</code></td><td><code>true</code></td><td>When set to <code>true</code>, an open slideout closes when the user hits the escape key. Otherwise, this keypress is ignored.</td></tr><tr><td>Close on outside click</td><td><code>boolean</code></td><td><code>true</code></td><td>When set to <code>true</code>, an open slideout closes when the user clicks outside its content, i.e. on the blurred background. It is recommended to disable this for complex slideouts, of which the user would not like to lose state easily.</td></tr><tr><td>Header text</td><td><code>stringWithJs</code></td><td><code>'Header'</code></td><td>The text to be displayed in the header. Only available when the "Show header" property is set to <code>true.</code></td></tr><tr><td>Position</td><td><code>'left' | 'right'</code></td><td><code>'right'</code></td><td>Defines the position of the slideout on the screen.</td></tr><tr><td>Scrollable</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether the slideout content is scrollable or not. When set to <code>false</code>, overflowing content is not be accessible for the end user.</td></tr><tr><td>Show close icon</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether there should be the "x" icon in the top-right corner for closing the slideout. In case "Show header" is set to <code>false</code>, the icon might overlap with the slideout content.</td></tr><tr><td>Show header</td><td><code>boolean</code></td><td><code>true</code></td><td>Determines whether the slideout should have a header. When set to <code>true</code>, the header text can be configured via the "Header text" property.</td></tr><tr><td>Visible</td><td><code>boolean</code></td><td><code>false</code></td><td>The "Visible" property behaves differently than in other components. For the <code>Slideout</code>, this property determines whether the slideout is open or closed. You might apply a dynamic JavaScript expression here, but usually a slideout should be opened or closed explicitly using the exposed functions.</td></tr><tr><td>Width</td><td><code>string</code></td><td><code>'60vw'</code></td><td>CSS width of the slideout. See considerations <a href="#undefined">above</a>.</td></tr></tbody></table>

Review the notes regarding the "Scrollable" property in the [Container](/component-reference/container) reference, the `Slideout` behaves in exactly the same way.

{% hint style="info" %}
An open slideout is closed automatically when another slideout or modal is opened. This means, that you do not need to close a slideout explicitly when opening another slideout or a modal.
{% endhint %}

### Events

The `Slidout` offers two events to associate actions with:

* **On show**: This action is executed whenever the slideout is opened, regardless of the reason ("Visible" property, `show()` function, selecting the component in the explorer tab of the app editor).
* **On hide**: This action is executed when a slideout is closed, regardless of the reason.

To maintain a pleasant user experience, the "On show" action is executed asynchronously with displaying the slideout. A long-running action does not block the slideout render, but leads to a spinner until the action run has finished. This is useful, for example, fetching data from sources when a slideout is opened.

The "On hide" action is executed asynchronously as well, i.e. the slideout will be closed instantly, but the action might still be running. Again, this leads to a more snappy UI and better user experience. There is currently no way to modify this action execution behavior.

### API

The `Slideout` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>blurredBackground</code></td><td><code>boolean</code></td><td>Current boolean value of the "Blurred background" property.</td></tr><tr><td><code>close()</code></td><td><code>() => void</code></td><td>Closes the slideout and executes the "On hide" action <a href="#events">asynchronously</a>. Noop in case the slideout is not open.</td></tr><tr><td><code>closeOnEscape</code></td><td><code>boolean</code></td><td>Current boolean value of the "Close on escape" property.</td></tr><tr><td><code>closeOnOutsideClick</code></td><td><code>boolean</code></td><td>Current boolean value of the "Close on outside click" property.</td></tr><tr><td><code>headerText</code></td><td><code>string</code></td><td>Current string value of the "Header text" configuration.</td></tr><tr><td><code>hide()</code></td><td><code>() => void</code></td><td>Alias for <code>close().</code></td></tr><tr><td><code>open()</code></td><td><code>() => void</code></td><td>Alias for <code>show()</code>.</td></tr><tr><td><code>setBlurredBackground(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Blurred background" property to the provided boolean value.</td></tr><tr><td><code>setCloseOnEscape(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Close on escape" property to the provided boolean value.</td></tr><tr><td><code>setCloseOnOutsideClick(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Close on outside click" property to the provided boolean value.</td></tr><tr><td><code>setHeaderText(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the header text to the provided string value.</td></tr><tr><td><code>setShowCloseIcon(value)</code></td><td><code>(value: boolean) => void</code></td><td>Defines if the close icon should be displayed or not.</td></tr><tr><td><code>setShowHeader(value)</code></td><td><code>(value: boolean) => void</code></td><td>Adjusts the flag whether the header text should be displayed to the provided boolean value.</td></tr><tr><td>setWidth(value)</td><td>(value: number) => void</td><td>Sets the "Width" property to the provided string value. The value should be a valid CSS expression. This is not validated.</td></tr><tr><td><code>show()</code></td><td><code>() => void</code></td><td>Opens the slideout and executes the "On show" action <a href="#events">asynchronously</a>. Closes any other slideout that is currently open. Noop in case the slideout is already open.</td></tr><tr><td><code>showCloseIcon</code></td><td><code>boolean</code></td><td>Current boolean value fo the "Show close icon" property.</td></tr><tr><td><code>showHeader</code></td><td><code>boolean</code></td><td>Current boolean value of the "Show header" configuration.</td></tr><tr><td><code>width</code></td><td><code>string</code></td><td>Current css-string value of the "Width" property.</td></tr></tbody></table>


# Switch

The `Switch` allows the user to make a basic "Yes or No" decision, i.e. it represents an input for a boolean value. This is useful either as part of a form, or as a stand-alone toggle for basic filters for tables and similar use cases.

<figure><img src="/files/G2pUY2T048wpWJA9pVjE" alt=""><figcaption><p>Switch component</p></figcaption></figure>

### Properties

The `Switch` component offers the following configuration properties.

<table><thead><tr><th width="158">Property</th><th width="159">Type</th><th width="161">Default value</th><th>Behavior</th></tr></thead><tbody><tr><td>Default value</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines whether the initial state of the switch should be active or not. If used with dynamic JavaScript expressions, this can reactively update the toggle based on specific conditions.</td></tr><tr><td>Disabled</td><td><code>boolean</code></td><td><code>false</code></td><td>Whether the switch should be disabled or not. When disabled, the input is greyed out and cannot be toggled.</td></tr><tr><td>Label</td><td><code>stringWithJs</code></td><td><code>'Switch'</code></td><td>Label that is rendered next to the toggle element.</td></tr><tr><td>Label alignment</td><td><code>'left' | 'center' | 'right'</code></td><td><code>'left'</code></td><td>Text alignment of the label.</td></tr><tr><td>Label position</td><td><code>'left' | 'right'</code></td><td><code>'left'</code></td><td>Determines whether the toggle should be placed to the left of the label or to the right.</td></tr><tr><td>Required</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines how the switch behaves as part of a form during submit. When set to <code>true</code>, the surrounding form submit fails with a validation error in case the switch is not activated.</td></tr><tr><td>Toggle with label click</td><td><code>boolean</code></td><td><code>false</code></td><td>Determines whether the switch can be toggled only by clicking the toggle directly (<code>false</code>), or also by clicking on the label (<code>true</code>)</td></tr></tbody></table>

### Events

The `Switch` offers three events, that are all related to the UI event when the switch is toggled:

* **On change**: This event is triggered whenever the switch is toggled, regardless whether activated or deactivated.
* **On false**: This event is triggered whenever the switch is deactivated. This is equivalent to an action running at "On change", with a condition like `if (!mySwitch.value) { ... }` inside.
* **On true**: This event is triggered whenever the switch is activated. This is equivalent to an action running at "On change", with a condition like `if (mySwitch.value) { ... }` inside.

### API

The `Switch` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>clear()</code></td><td><code>() => void</code></td><td>Resets the switch to inactive (<code>value</code> being <code>false</code>)</td></tr><tr><td> <code>disabled</code></td><td><code>boolean</code></td><td>Current boolean value of the "Disabled" property.</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>Current string value of the "Label" property.</td></tr><tr><td><code>reset()</code></td><td><code>() => void</code></td><td>Resets the switch similar to the <code>clear()</code> function, however not always to <code>false</code>, but to the value of the "Default value" property. This is usually used to reset a form to a default state, instead of completely clearing it.</td></tr><tr><td><code>setDisabled(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the value of the "Disabled" property to the provided boolean value.</td></tr><tr><td><code>setLabel(value)</code></td><td><code>(value: string) => void</code></td><td>Sets the value of the "Label" property to the provided string value.</td></tr><tr><td><code>setValue(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the toggle-state to the provided boolean value. This does not trigger any events.</td></tr><tr><td><code>value</code></td><td><code>boolean</code></td><td>Current boolean value representing the toggle-state of the switch. Will return <code>true</code> if active, and <code>false</code> if inactive.</td></tr></tbody></table>


# Table

### API

The `Table` exposes the following API in the JavaScript runtime environment.

<table><thead><tr><th width="271">Property / Function</th><th width="210">Type</th><th>Behavior</th></tr></thead><tbody><tr><td><code>addRows(value)</code></td><td><code>(value: Object | Object[]) => {addedRowIds: number[]}</code></td><td>Appends a new row or a list of new rows to the data set. It takes an object that describes the row content containing all column fields or an array of those. <strong>Note:</strong> It does not automatically trigger the action linked to "On row add".</td></tr><tr><td> <code>columnTypeIcons</code></td><td><code>boolean</code></td><td>Current boolean value of the "Show data type icons" property.</td></tr><tr><td><code>deleteRows(value)</code></td><td><code>(value: (string | number) | (string | number)[]) => {deletedRowIds: number[]}</code></td><td>Removes row(s) with the provided primary key(s). <strong>Note:</strong> It does not automatically trigger the action linked to "On row add".</td></tr><tr><td><code>filteringEnabled</code></td><td><code>boolean</code></td><td>Current boolean value of the "Filtering" property.</td></tr><tr><td><code>horizontalDividers</code></td><td><code>boolean</code></td><td>Current boolean value of the "Show horizontal dividers" property.</td></tr><tr><td><code>lastClickedRow</code></td><td><code>Object</code></td><td>Returns the row that was last clicked on as an object with the columns as keys.</td></tr><tr><td><code>multiRowSelection</code></td><td><code>boolean</code></td><td>Current boolean value of the "Multi-row selection" property.</td></tr><tr><td><code>newRecords</code></td><td><code>Object[]</code></td><td>Returns all rows that have been added to the table component before they have been saved (i.e. staged changes).</td></tr><tr><td><code>primaryKey</code></td><td><code>any</code></td><td>Returns the primary key that is in use in the table. The primary is either inferred from the underlying data source or explicitly set in the table.</td></tr><tr><td><code>recordCount</code></td><td><code>number</code></td><td>Returns how many records are in the table. It excludes all changes that have not been saved yet.</td></tr><tr><td><code>refresh()</code></td><td><code>() => void</code></td><td>Executes the action that is specified in the event handler "On refresh click"</td></tr><tr><td><code>rowIds</code></td><td><code>any[]</code></td><td>Returns a list of the primary keys present in table data.</td></tr><tr><td><code>rowsById</code></td><td><code>Object</code></td><td>Returns an object of objects containing all records in the table. The records are indexed by their primary key.</td></tr><tr><td><code>selectedRows</code></td><td><code>Object[]</code></td><td>Returns all rows that are currently selected.</td></tr><tr><td><code>setColumnTypeIcons(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Show data type icons" property to the provided value.</td></tr><tr><td><code>setData(value)</code></td><td><code>(value: Object[]) => void</code></td><td>It sets the data that is displayed in the table component and takes an array of objects with the column headers as keys. </td></tr><tr><td><code>setFilteringEnabled(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Filtering" property to the provided value.</td></tr><tr><td><code>setHorizontalDividers(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Show horizontal dividers" property to the provided value.</td></tr><tr><td><code>setMultiRowSelection(value)</code></td><td><code>(value: boolean) => void</code></td><td>Sets the "Multi-row selection" property to the provided value.</td></tr><tr><td><code>setSelectedRows(value)</code></td><td><code>(value: Function) => boolean</code></td><td>Selects all the rows for which the provided function evaluates to <code>true</code>. The function must be of the following type:  <code>(row: Object, rowIndex: number) => boolean</code></td></tr><tr><td><code>setShowRefreshButton(value)</code></td><td><code>(value: boolean) => row</code></td><td>Sets the "Show refresh button" property to the provided value.</td></tr><tr><td><code>setShowResultNumber(value)</code></td><td><code>(value: boolean) => row</code></td><td>Sets the "Show number of results" property to the provided value.</td></tr><tr><td><code>setSortingEnabled(value)</code></td><td><code>(value: boolean) => row</code></td><td>Sets the "Sorting" property to the provided value.</td></tr><tr><td><code>setTextSearchEnabled(value)</code></td><td><code>(value: boolean) => row</code></td><td>Sets the "Text search enables" property to the provided value.</td></tr><tr><td><code>setVerticalDividers(value)</code></td><td><code>(value: boolean) => row</code></td><td>Sets the "Show vertical dividers" property to the provided value.</td></tr><tr><td><code>setVisible</code></td><td><code>(value: boolean) => row</code></td><td>Sets the "Visible" property to the provided value.</td></tr><tr><td><code>showRefreshButton</code></td><td><code>boolean</code></td><td>Current boolean value of the "Show refresh button" property.</td></tr><tr><td><code>showResultNumber</code></td><td><code>boolean</code></td><td>Current boolean value of the "Show number of results" property.</td></tr><tr><td><code>sortingEnabled</code></td><td><code>boolean</code></td><td>Current boolean value of the "Sorting" property.</td></tr><tr><td><code>stagedChanges</code></td><td><code>{ rowId: number, rowIndex: number, oldRowData: Object, newRowData: Object, row: Object, changes: Object }</code></td><td>Returns the unsaved changes to the data in the table.</td></tr><tr><td><code>textSearchEnabled</code></td><td><code>boolean</code></td><td>Current boolean value of the "Text search enabled" property.</td></tr><tr><td><code>updateOrAddRows(value)</code></td><td><code>(value: Object | Object[]) => {addedRowIds: number[], updatedRowIds: number[]}</code></td><td>Takes an record or an array of records that should be added or updated and modifies the table data. For updates it expects an object containing the primary and all columns that should be updated while it expects an object with all columns for rows that should be added. <strong>Note:</strong> It does not automatically trigger the actions linked to "On row add" and "On row update".</td></tr><tr><td><code>updateRows(value)</code></td><td><code>(value: Object | Object[]) => {updatedRowIds: number[]}</code></td><td>Updates a record or a list of records in the data set. It takes an object that describes the row content containing the primary key and all columns that should be updated. <strong>Note:</strong> It does not automatically trigger the actions linked to "On row add" and "On row update".</td></tr><tr><td><code>verticalDividers</code></td><td><code>boolean</code></td><td>Current boolean value of the "Show vertical dividers" property.</td></tr><tr><td><code>visible</code></td><td><code>boolean</code></td><td>Current boolean value of the "Visible" property.</td></tr></tbody></table>


