> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hyperwisor.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Form

> A multi-field input form that submits all its fields as command params — with firmware read.

<Info>
  **Category:** Controls · **Sends commands:** yes · **Emits a value:** yes (auto-injected on submit)
</Info>

## Mental model

A **Form is a settings sheet.** The user fills several fields and hits submit, and
all of them travel together to the device — keyed by field ID. It's the go-to when
you need to send *several values at once*.

## When to use it

* Collect multiple values and send them in one command (config, calibration)
* Any "fill these in, then apply" interaction

## Settings

### General — per field

Each field has:

* **Field ID** — the key the value is sent under (read this in firmware)
* **Field Label** · **Field Type**
* **Default value** · **Max length** · **Max value** — validation

### Style

Background, border, focus / error border, font, spacing per field.

### Triggers

| Event              | Fires when                                                |
| ------------------ | --------------------------------------------------------- |
| `submit`           | The form is submitted (auto-injects all field values)     |
| `input` / `change` | A field changes                                           |
| `focus` / `blur`   | Field focus changes                                       |
| *(common)*         | `load`, `ready`, `destroy`, `update`, `visible`, `hidden` |

## Auto-injected fields on submit

On `submit`, the Form **injects every field value into the action's params**, keyed
by field ID — merged with any static params. See
[emitted values](/dashboard/triggers#widgets-that-emit-a-value-auto-injected-params).

**Trigger:**

```
WHEN     submit
SEND TO  Current Device
EXECUTE  Config
Action   save   params: {}     ← device receives { "<fieldId>": <value>, … }
```

**Firmware** (Arduino) — read each field by its ID:

```cpp theme={null}
device.setUserCommandHandler([](JsonObject &msg) {
  JsonObject p = device.findParams(msg, "Config", "save");
  if (!p.isNull()) {
    const char* name  = p["deviceName"];   // field IDs
    int         limit = p["maxTemp"];
    // apply the submitted settings
  }
});
```

## Script API example

The same interactions from a [dashboard script](/script/overview):

```js theme={null}
widget.on('configForm', 'submit', async (fields) => {
  await db.insert('config_history', fields);   // archive every submission
  widget.setText('formStatus', 'Saved \u2713');
});
```

<CardGroup cols={2}>
  <Card title="Database Form" icon="database" href="/dashboard/widgets/database-form">
    Save form input straight to the database.
  </Card>

  <Card title="Emitted values" icon="arrow-right-arrow-left" href="/dashboard/triggers#widgets-that-emit-a-value-auto-injected-params">
    How fields auto-inject.
  </Card>
</CardGroup>
