Form
<!-- generated:meta -->
| Grup | ui |
| Plane | resource |
| Spec struct | FormSpec |
<!-- /generated:meta -->
Kapan Memakai
kind: Form adalah override layout input/edit untuk satu Entity — menggantikan form hasil derivasi otomatis.
Kapan memakai Form:
- Urutan/label/hide field berbeda dari default
- Grouping field per section (
sections), multi-kolom visible_when/readonly_when/required_when/compute(FormSpecExpr)- Ubah container:
rendermodal / drawer / separate_page
Kapan TIDAK pakai Form:
- Entity cukup dengan default → jangan deklarasi Form sama sekali
- Komposisi multi-entity →
kind: Page
Prinsip 3-layer: Form/Table adalah layer tengah antara Entity dan Page. Table = bentuk lain dari Form (sama-sama override di layer yang sama).
Sumber kontrak: docs/spec/frontend/06-page-kinds.md §2.
Contoh Manifest
apiVersion: formspec.dev/v1
kind: Form
metadata:
name: order-edit
module: billing
spec:
public: true
entity: billing.order
mode: edit # create | edit | view
render: { mode: separate_page } # modal | drawer | separate_page
sections:
- title: Customer
columns: 2
fields:
- { field: customer_id, widget: relation-picker }
- { field: member_tier, read_only: true }
- title: Totals
fields:
- {
field: total,
read_only: true,
compute: "sum([i.quantity * i.price for i in fields.items])",
}
actions:
- { action: checkout, label: "Checkout", style: primary }Atribut
<!-- generated:attributes -->
| Atribut | Tipe | Wajib | Contoh | Deskripsi |
|---|---|---|---|---|
public | boolean | — | true | If true (default), a route /module/form/<name> is auto-generated. Set false for embed-only forms. |
entity | string | ✅ | billing.order | |
auth_action | enum (login · register · change_password · forgot_password · reset_password) | — | Bind submit to a platform auth action (mutually exclusive with entity). | |
mode | enum (create · edit · view) | — | edit | |
sections | []FormSection | — | ||
actions | []FormAction | — | ||
submit | FormSubmit | — | ||
render | FormRenderDecl | — | ||
context | []ContextDecl | — | Context declares render-context variables injected into this form's | |
confirm | FormConfirm | — | Per-form confirm override: create/update message — nil = inherit App default, empty string = off |
<!-- /generated:attributes -->
Render Context (standard slots)
Form menerima standard slots fields (nilai form saat ini), route (route.params.*, route.query.*, route.path), dan user (identitas session). Tambahan variabel via context: (closed source set: session, entity, api, const, expr, config).
Auth Forms (auth_action)
Form TANPA entity yang mendeklarasikan auth_action (closed set: login | register | change_password | forgot_password | reset_password) me-render custom auth screen pure-YAML: submit di-dispatch ke endpoint /_ui/auth/* (bukan entity CRUD). Nama field memetakan konvensional ke payload auth: username, password, current_password, new_password, email, display_name, token. Sukses login/register → session boot + redirect {route.query.returnTo} (same-origin guard). forgot_password dan reset_password cocok untuk halaman public (Page.public: true).
Tiap field juga mendapat atribut autocomplete konvensional dari pasangan auth_action × nama field: username → username, password pada login → current-password, password pada register/reset_password → new-password, email → email, display_name → name; sisanya off. Tanpa token ini password manager tidak bisa memasangkan, mengisi, atau menyimpan kredensial dengan benar — guidance: Chromium — Create Amazing Password Forms. Karena konvensional, tidak ada properti YAML baru untuk ini.
Gotchas
- Tiap
fieldwajib ada di Entity; tiapactionwajib ada + permission-gated otomatis. helpmewarisidescriptionentity —FormField.helpdanField.descriptionberarti sama bagi pengguna;helpdi form hanya perlu ditulis untuk override per-form. Konsekuensinyadescriptionentity adalah teks yang dibaca pengguna akhir, bukan catatan desain — detail implementasi ditulis sebagai komentar YAML (# …).help/descriptionhanya tampil di mode create/edit, tidak di modeview, dan tidak di permukaan baca-saja (Table/detail). Section pertama juga mewarisimetadata.descriptionentity sebagai subtitle drawer/dialog (docs/spec/frontend/06-page-kinds.md§2).- Vocabulary perilaku client TERTUTUP:
visible_when,readonly_when,required_when,compute— butuh efek imperatif → custom widget (asset), bukan FormSpecExpr. - Form auth wajib token
autocompletestandar (username,current-password,new-password) — jangan"nope"(token tidak valid) atau"off"pada field kredensial; password manager berhenti bisa memasangkan/menyimpan. render= keputusan container design-time, bukan runtime.modal(≤5 field),drawer(5–12),separate_page(12+ field / child table / butuh deep-link). Form kedua dengan render lain = deklarasi terpisah.- Validasi server tetap otoritas —
rulesdi client untuk UX, bukan keamanan. public: false→ embed-only (no route); hanya tampil di Page authored.- Pola UI dipilih dari
submitaktif/tidak (bukancharacteristic): 2-step+autosave (default), 2-step manual, atau 1-stepcreate-submit. - Cross-ref:
docs/spec/frontend/06-page-kinds.md§2 ·ai_skills/formspec-kinds