Layer 5 — Forms. Designed for import Rad.Form as Form.
Opaque membership record. Constructed by field / validatedField.
The persisted state of a form: snapshot blob + submit-lifecycle counters.
Codec for State, useful at persistence boundaries and in tests.
Allocate a Form's persisted state cell. Initial value
{ snapshot = null, submitSeq = 0, lastResolvedSubmitSeq = 0 }.
init =
build Model
|> with "name" "" stringCodec
|> Form.withState "profile-form"Construct a Form fields value bundling state cell, fields, and members.
Pure — call per render.
Mark a ValidatedCell as a form field. Captures input/validation/
activation-seq ids and the member's validation reaction.
True iff any member's current registry value differs from the form's
pristine reference (snapshot when non-null, otherwise each member's initial).
Bumps submitSeq and dispatches validate to each ValidatedMember.
Restores all members to pristine value (snapshot or initial), zeros each ValidatedMember's activation seq, sets validation slot to Dormant. Does NOT modify submitSeq / lastResolvedSubmitSeq.
One reaction per ValidatedMember; PlainMembers contribute nothing.
Concatenate with the user's other reactions in AppDef.reactions.
A reaction that fires a Request when the form is submitted and all
validators in the group are Valid. On success, advances the form's
snapshot and lastResolvedSubmitSeq. The target cell is Cell (Remote err r).
Like onSubmit, but runs an Action instead of dispatching a Request. On
each fire (when the gate passes), bumps lastResolvedSubmitSeq to
submitSeq and advances the snapshot.
Bundled snapshot of the form's UI state. Suitable for rendering a submit
button. See status for precedence.
Bundled Status. Precedence: invalid → HasErrors; submitPending+checking
→ Validating; submitPending → Submitting; dirty → Editable; else Pristine.
dirty AND not invalid AND not checking AND not submitPending.
True iff submitSeq > lastResolvedSubmitSeq.
True iff any ValidatedMember's validation slot decodes to Invalid.
True iff any ValidatedMember's validation slot decodes to Checking.
Number of members in the form. For tests.
Typed bundle of validators in a form, used to gate submission.
Group with a single validator. clean is the input type of the field.
Group with two validators. clean = (a, b).
Group with three validators.
Group with four validators. The packer function receives the four clean
values in order and returns the typed clean type (typically a record).
validators4 (\name email age role -> { name = name, email = email, age = age, role = role })
.name
.email
.age
.roleGroup with five validators. The packer function receives the five clean
values in order and returns the typed clean type (typically a record).
validators5 (\name email age role active -> { name = name, email = email, age = age, role = role, active = active })
.name
.email
.age
.role
.activeGroup with six validators. The packer function receives the six clean
values in order and returns the typed clean type (typically a record).
validators6 (\a b c d e f -> { fieldA = a, fieldB = b, fieldC = c, fieldD = d, fieldE = e, fieldF = f })
.fieldA
.fieldB
.fieldC
.fieldD
.fieldE
.fieldFGroup with seven validators. The packer function receives the seven clean
values in order and returns the typed clean type (typically a record).
validators7 (\a b c d e f g -> { fieldA = a, fieldB = b, fieldC = c, fieldD = d, fieldE = e, fieldF = f, fieldG = g })
.fieldA
.fieldB
.fieldC
.fieldD
.fieldE
.fieldF
.fieldGGroup with eight validators. The packer function receives the eight clean
values in order and returns the typed clean type (typically a record).
validators8 (\a b c d e f g h -> { fieldA = a, fieldB = b, fieldC = c, fieldD = d, fieldE = e, fieldF = f, fieldG = g, fieldH = h })
.fieldA
.fieldB
.fieldC
.fieldD
.fieldE
.fieldF
.fieldG
.fieldHTransform a group's clean type. Useful for packing tuples into records.
Read a group's clean values from registry. Returns Just clean iff every
validator is Valid. Mostly for tests.
Opaque transaction boundary over a cells record.