We use a library to build forms that take care of the most tedious work for us.
elm-form-components
)Forms are based on two parts, a configuration and a state:
type alias Model =
{ conf : R10.Form.Conf.Conf
, state : R10.Form.State
}
These parts also contains field state and configuration for a total of four things:
Configuration contains all the static stuff that describe a form and doesn't change while a user is editing it.
The configuration is just a list of entities:
type alias Conf = List Entity
where Entity
is a recursive basic block of the form. Entity
can be an input field or a container for multiple input fields. It can also be a separator, like a title or a sub-title.
type Entity
= EntityNormal EntityId (List Entity)
| EntityWrappable EntityId (List Entity)
| R10.Form.entity.withBorder EntityId (List Entity)
| R10.Form.entity.withTabs EntityId (List Entity)
| R10.Form.entity.multi EntityId (List Entity)
| EntityTitle TextSection
| R10.Form.entity.subTitle TextSection
| R10.Form.entity.field R10.Form.FieldConf.FieldConf
The form is like a tree where leafs are the actually input fields.
The field configuration contain all static data that describe an input field:
type alias FieldConf =
{ id : String
, type_ : FieldType
, label : String
, helperText : Maybe String
, required : Bool
, minLength : Maybe Int
, maxLength : Maybe Int
, regexValidations : List RegexValidation
}
The form state contain all data that is changing, both at the form level and at the fields level
type alias State =
{ fieldState : Dict.Dict String R10.Form.FieldState.FieldState
, quantities : Quantities
, activeTabs : ActiveTabs
, focused : Maybe String
, removed : Set.Set String
, qtySubmitAttempted : QtySubmitAttempted
}
In details
fieldState
is a dictionary where: the key is a special value generated by the library containing information about the position of the input field in the tree and the value is the field state.quantities
keep the count of how many quantity a multiplicable entity has been multipliedactiveTabs
is used to rememebr which tab is active when R10.Form.entity.withTabs
is usedfocuesed
store the key of the input field that has the focusremoved
keep track of all items that have been removed from an multiplicable entityqtySubmitAttempted
counts home many time a form has been submitted. If zero, the from has never been submittedField state contain data that can change in an input field
type alias FieldState =
{ lostFocusOneOrMoreTime : Bool
, value : String
, dirty : Bool -- Updated but not used
, disabled : Bool
, validation : Validation
, showPassword : Bool -- Used only for passwords
}
Field types are grouped in 4 categories:
type FieldType
= TypeText TypeText
| TypeBinary TypeBinary
| TypeSingle TypeSingle (List FieldOption)
| TypeMulti TypeMulti (List FieldOption)
TypeText
is for types where the user is typing some text. Subtypes belonging to this category are:
type TypeText
= TextPlain
| TextEmail
| TextUsername
| TextPasswordNew
| TextPasswordCurrent
| TextMultiline
TypeBinary
is for type that allow two choices. Subtypes are:
type TypeBinary
= BinaryCheckbox
| BinarySwitch
TypeSingle
is for types that allow a single choice. Subtypes are:
type TypeSingle
= SingleRadio
| SingleCombobox
TypeMulti
is for types that allow multiple choices (not to be confused with multiline input text and not with multiple checkboxes). Subtypes are:
type TypeMulti
= MultiCombobox -- TODO
Keys are used to indetify nodes in the tree (and consequently, input fields). They are stored as a list of EntityId
that are strings:
type Key = Key (List String)
The are converted to string just joining all the element of the list and using a separator, usually "/".
The library provide a built in system to manage message and state changes. So, to add a form into a page is enough to create one message. You can see details in this page
"Makers" are script that take the form configuration and transorm it in something lese, called Outcome
.
This maker generate Element R10.Form.Msg
that is a nested view structure to display the form on the screen
This maker generate ( R10.Form.Key.Key, R10.Form.FieldConf.ValidationSpecs )
that is a flat list of all keys and validation configuration of entities.
This maker generate Form.StateForValues.Entity
that is a shallow (but not flat) structure that contain all the values of the form. This structure could be close to a graphQL structure. Could also generate type (String, Bool, Int,etc.) accordingly if needed.
See below few example of forms.
Both FormState
and FormConf
have decoder and encoders so you can edit their JSON version below and see the result in realtime.