# Field dan validasi

> Definisikan field sekali dan Ikiform memvalidasi setiap kiriman di server, dengan pesan yang jelas dan nilai bertipe.

Field bersifat opsional. Formulir tanpa field menerima data apa pun yang dikirim formulir Anda. Begitu Anda memublikasikan field, Ikiform memvalidasi setiap kiriman terhadapnya di server, menolak data yang salah dengan pesan yang jelas, dan menyimpan nilai yang bersih dan bertipe.

## Mendefinisikan field

Buka formulir di dashboard dan masuk ke **Fields**. Tambahkan field di editor visual atau edit definisi JSON secara langsung. `id` setiap field adalah `name` input yang harus dikirim formulir Anda.

`Definisi formulir`:

```json
{
  "version": 1,
  "title": "Contact",
  "fields": [
    { "id": "name", "type": "text", "label": "Name", "required": true, "validation": { "max_length": 120 } },
    { "id": "email", "type": "email", "label": "Email", "required": true },
    {
      "id": "topic",
      "type": "select",
      "label": "Topic",
      "options": [
        { "value": "sales", "label": "Sales" },
        { "value": "support", "label": "Support" }
      ]
    },
    { "id": "message", "type": "textarea", "label": "Message", "validation": { "min_length": 10 } }
  ],
  "settings": { "submit_label": "Send", "success_message": "Thanks, we'll reply within a day." }
}
```

### Definisi

| Key | Wajib | Aturan |
| --- | --- | --- |
| `version` | Ya | Selalu `1` |
| `title` | Ya | 1 sampai 200 karakter |
| `description` | Tidak | Hingga 2.000 karakter |
| `fields` | Ya | 1 sampai 100 field dengan ID unik, maksimal 2.000 opsi secara total |
| `settings.submit_label` | Tidak | 1 sampai 100 karakter, dipakai oleh renderer seperti [komponen shadcn/ui](https://ikiform.com/id/docs/shadcn.md) |
| `settings.success_message` | Tidak | 1 sampai 1.000 karakter |
| `settings.redirect_url` | Tidak | URL `http:` atau `https:` hingga 2.048 karakter |
| `$schema` | Tidak | String apa pun hingga 200 karakter, diabaikan |

Key yang tidak dikenal ditolak, baik di level teratas maupun di setiap field, jadi salah ketik seperti `requried` muncul sebagai error saat Anda menyimpan.

### Key di setiap field

| Key | Wajib | Aturan |
| --- | --- | --- |
| `id` | Ya | snake_case huruf kecil, diawali huruf, maksimal 64 karakter, misalnya `team_size` |
| `type` | Ya | Salah satu dari 12 tipe di bawah |
| `label` | Ya | 1 sampai 200 karakter. Dipakai di halaman error dan email notifikasi |
| `required` | Tidak | `true` membuat nilai kosong menjadi error |
| `placeholder` | Tidak | Hingga 200 karakter |
| `help_text` | Tidak | Hingga 1.000 karakter |

## Playground

Playground interaktif di halaman ini memungkinkan Anda mengedit definisi formulir dan contoh payload JSON. Playground menjalankan validator Ikiform yang sebenarnya di browser dan menampilkan respons `201` atau `422` persis seperti yang akan dikembalikan server, nilai tersimpan yang sudah dikonversi, dan markup HTML yang dihasilkan untuk formulir.

## Tipe field

| Tipe | Disimpan sebagai | Menerima |
| --- | --- | --- |
| `text` | string | Teks satu baris apa pun |
| `textarea` | string | Teks multi-baris |
| `email` | string, domain dijadikan huruf kecil | Satu alamat email |
| `number` | number | Angka, atau string numerik dari formulir HTML |
| `select` | string | Satu nilai opsi yang tercantum |
| `radio` | string | Satu nilai opsi yang tercantum |
| `multi_select` | string[] | Nilai opsi yang tercantum, duplikat dihapus |
| `checkbox` | boolean | `on`, `true`, `1`, `yes`, atau kosong |
| `date` | string, `YYYY-MM-DD` | Tanggal kalender yang valid |
| `url` | string, dinormalisasi | Alamat web, `https://` ditambahkan jika tidak ada |
| `phone` | string | Angka, spasi, `+ ( ) . -` |
| `rating` | integer | Bilangan bulat dari 1 sampai `max` |

### text

| validation | Nilai | Default |
| --- | --- | --- |
| `min_length` | 0 sampai 10.000 | Tidak ada |
| `max_length` | 1 sampai 10.000, atau maksimal 256 dengan pattern | 1.000, atau 256 dengan pattern |
| `pattern` | Regular expression hingga 200 karakter, dicocokkan dengan seluruh nilai | Tidak ada |
| `pattern_message` | 1 sampai 200 karakter | `Invalid format` |

Pattern berjalan dengan flag `u` dan harus cocok dengan seluruh nilai. Saat Anda menyimpan, Ikiform menolak pattern yang bisa mengalami backtracking katastrofik: backreference, grup berulang yang berisi pengulangan atau alternatif (`(a+)+`, `(a|b)*`), dan terlalu banyak pengulangan bertingkat.

### textarea

| Key | Nilai | Default |
| --- | --- | --- |
| `validation.min_length` | 0 sampai 16.000 | Tidak ada |
| `validation.max_length` | 1 sampai 16.000 | 10,000 |
| `rows` | 1 sampai 50, petunjuk tampilan untuk renderer | Tidak ada |

### email

| validation | Nilai | Default |
| --- | --- | --- |
| `allowed_domains` | 1 sampai 100 domain seperti `example.com`, dicocokkan persis | Domain apa pun |

Alamat lebih dari 254 karakter ditolak. Bagian domain disimpan dalam huruf kecil; bagian sebelum `@` disimpan seperti yang dikirim.

### number

| validation | Nilai | Default |
| --- | --- | --- |
| `min` | Angka apa pun | Tidak ada |
| `max` | Angka apa pun, minimal `min` | Tidak ada |
| `integer` | `true` untuk menolak desimal | `false` |

### select, radio, dan multi_select

| Key | Nilai | Default |
| --- | --- | --- |
| `options` | 1 sampai 500 objek `{ "value", "label" }` dengan nilai unik, masing-masing 1 sampai 200 karakter | Wajib |
| `validation.min_items` | Hanya `multi_select`, 0 sampai 500 | 0, atau 1 jika wajib |
| `validation.max_items` | Hanya `multi_select`, 1 sampai 500 | Jumlah opsi |

Hanya nilai yang tercantum yang diterima. `select` dan `radio` menerima satu nilai; kirim `multi_select` sebagai nama berulang, `name[]`, atau array JSON.

### checkbox

Tanpa key tambahan. Checkbox yang tidak ada bernilai `false`. `required: true` berarti harus dicentang, cocok untuk kotak persetujuan. Nilai selain `on`, `true`, `1`, `yes`, `off`, `false`, `0`, `no`, atau kosong ditolak.

### date

| validation | Nilai | Default |
| --- | --- | --- |
| `min` | Tanggal `YYYY-MM-DD` | Tidak ada |
| `max` | Tanggal `YYYY-MM-DD`, sama dengan atau setelah `min` | Tidak ada |

### url

| validation | Nilai | Default |
| --- | --- | --- |
| `https_only` | `true` untuk menolak `http:` | `false` |
| `max_length` | 1 sampai 4.096 | 2,048 |

Hanya URL `http:` dan `https:` dengan hostname bertitik dan tanpa nama pengguna atau kata sandi yang diterima. Nilai yang disimpan adalah URL yang sudah dinormalisasi.

### phone

| validation | Nilai | Default |
| --- | --- | --- |
| `e164` | `true` untuk mewajibkan format internasional, disimpan tanpa pemisah | `false` |

Tanpa `e164`, nomor telepon butuh 6 sampai 15 digit dan disimpan seperti yang dikirim. Dengannya, `+1 (415) 555-0123` disimpan sebagai `+14155550123`.

### rating

| validation | Nilai | Default |
| --- | --- | --- |
| `max` | 2 sampai 10 | 5 |

## Konversi tipe

Formulir HTML mengirim semuanya sebagai teks. Ikiform mengubahnya ke tipe yang dideklarasikan setiap field sebelum disimpan. Dengan `name` sebagai text, `email`, `guests` sebagai number, `newsletter` sebagai checkbox, `site` sebagai URL, dan `phone` sebagai field telepon:

`Dikirim, urlencoded`:

```text
name=++Ada+Lovelace++&email=Ada@Example.COM&guests=3&newsletter=on&site=example.com&phone=%2B1%20415%20555%200123
```

`Disimpan, dengan phone diatur ke e164`:

```json
{
  "name": "Ada Lovelace",
  "email": "Ada@example.com",
  "guests": 3,
  "newsletter": true,
  "site": "https://example.com/",
  "phone": "+14155550123"
}
```

- Nilai di-trim. String kosong dianggap tidak ada.
- Field yang mengharapkan satu nilai menolak array berisi lebih dari satu item.
- Angka menerima notasi desimal biasa dan eksponen hingga 64 karakter, seperti `3`, `-2.5`, atau `1e3`.

## Error validasi

Kiriman yang gagal validasi ditolak dengan `422 validation_failed` dan tidak ada yang disimpan. `error.fields` memetakan setiap ID field ke pesannya. Kiriman dari browser melihat pesan yang sama di halaman error hosted, dengan label field Anda.

`422 Unprocessable Content`:

```json
{
  "ok": false,
  "error": {
    "code": "validation_failed",
    "message": "Some fields are invalid.",
    "fields": {
      "email": ["Enter a valid email address"],
      "website": ["Unknown field \"website\""]
    }
  },
  "request_id": "req_01k7v2c9t3e8m5q4x6z0a1b2c3"
}
```

### Field yang tidak dikenal

Jika field didefinisikan, field apa pun yang tidak ada di definisi ditolak sebagai `Unknown field "name"`. Field reserved yang diawali `_`, honeypot, dan `cf-turnstile-response` tidak dihitung. Tanpa definisi, nama field apa pun diterima.

### Pesan

| Masalah | Pesan |
| --- | --- |
| Nilai wajib tidak ada | `This field is required` |
| Checkbox wajib tidak dicentang | `This box must be checked` |
| Terlalu pendek atau terlalu panjang | `Must be at least 10 characters`, `Must be at most 120 characters` |
| Pattern tidak cocok | `pattern_message` Anda, atau `Invalid format` |
| Email | `Enter a valid email address`, `Email must be at one of: example.com` |
| Angka | `Enter a number`, `Enter a whole number`, `Must be at least 1` |
| Opsi | `Choose one of the listed options`, `Choose at least 2 options`, `Choose at most 3 options` |
| Tanggal | `Enter a valid date (YYYY-MM-DD)`, `Must be on or after 2026-01-01` |
| URL | `Enter a valid URL`, `Enter a valid https:// URL` |
| Telepon | `Enter a valid phone number`, `Use international format, e.g. +14155550123` |
| Rating | `Choose a rating`, `Rating must be at most 5` |

## Validasi di browser

`validate` dari SDK meniru aturan dan pesan server untuk ke-12 tipe field, jadi Anda bisa menampilkan error sebelum mengirim. Server tetap menjadi sumber kebenaran. Lihat [validate](https://ikiform.com/id/docs/sdk.md#validate).
