type Model =
{ // ...
Firstname : string
FirstnameError : string }
type Msg =
// ...
| ChangeFirstname of string
let private init _ =
{ // ...
Firstname = ""
FirstnameError = "" }, Cmd.none
let private update msg model =
match msg with
// ...
| ChangeFirstname newValue ->
if newValue <> "" then
{ model with Firstname = newValue
FirstnameError = "" }, Cmd.none
else
{ model with Firstname = newValue
FirstnameError = "This field is required" }, Cmd.none
let private view model dispatch =
div [ ]
[ // ...
Field.div [ ]
[ Label.label [ ]
[ str "Firstname" ]
Control.div [ ]
[ Input.input [ Input.Value model.Firstname
Input.Placeholder "Ex: Maxime"
Input.OnChange (fun ev ->
ev.Value |> string |> ChangeFirstname |> dispatch
) ] ]
Help.help [ Help.Color IsDanger ]
[ str model.FirstnameError ] ]
// ...
]
Managing an input with Elmish
23 lines per input
Can a library solve the form problems ?
macro feature analys
type ValidationState =
| Valid
| Invalid of string
type ValidationState =
| Valid
| Invalid of string
type InputState =
{ Label : string
Placeholder : string option
Value : string
Validators : InputValidator list
ValidationInputState : ValidationState }
type ValidationState =
| Valid
| Invalid of string
type InputState =
{ Label : string
Placeholder : string option
Value : string
Validators : InputValidator list
ValidationInputState : ValidationState }
and InputValidator = InputState -> ValidationState
type ValidationState =
| Valid
| Invalid of string
type InputState =
{ Label : string
Placeholder : string option
Value : string
Validators : InputValidator list
ValidationInputState : ValidationState }
and InputValidator = InputState -> ValidationState
type FormState<'AppMsg> =
{ Fields : (Guid * InputState) list
OnFormMsg : (Msg -> 'AppMsg) option }
let form =
{ Fields =
[ (Guid.NewGuid(), { Label = "Firstname"
Placeholder = None
Value = ""
Validators = []
ValidationInputState = Valid })
(Guid.NewGuid(), { Label = "Lastname"
Placeholder = Some "Ex: Mangel"
Value = ""
Validators = [ isRequired ]
ValidationInputState = Valid }) ]
OnFormMsg = Some OnFormChange }
let private createForm =
let firstname =
input {
label "Firstname"
placeholder "Ex: Maxime"
isRequired
}
let surname =
input {
label "Surname"
placeholder "Ex: Mangel"
isRequired
}
form {
addInput firstname
addInput surname
}
let private createForm =
Form.create ()
|> Form.addInput
( Form.Input.create ()
|> Form.Input.label "Firstname"
|> Form.Input.placeholder "Ex: Maxime"
|> Form.Input.isRequired )
|> Form.addInput
( Form.Input.create ()
|> Form.Input.label "Surname"
|> Form.Input.placeholder "Ex: Mangel"
|> Form.Input.isRequired )
Computation Expression
Pipeline
Fluent
let private createForm =
Form<Msg>
.Create(OnFormMsg)
.AddField(
BasicInput
.Create()
.WithLabel("Firstname")
.WithPlaceholder("Ex: Maxime")
.IsRequired()
)
.AddField(
BasicInput
.Create()
.WithLabel("Surname")
.WithPlaceholder("Ex: Mangel")
.IsRequired()
)
Before
After
23 lines per input
7 lines per input
14 lines for configuring the form
Everything is write in one place:
in the library
Testing the whole features of a Form
type Key = string
type SelectState =
{ Label : string
SelectedKey : Key option
Values : (Key * string) list
Placeholder : (Key * string) option
Validators : SelectValidator list
ValidationSelectState : ValidationState }
type Key = string
type SelectState =
{ Label : string
SelectedKey : Key option
Values : (Key * string) list
Placeholder : (Key * string) option
Validators : SelectValidator list
ValidationSelectState : ValidationState
IsLoading : bool
ValuesFromServer : JS.Promise<(Key * string) list> option }
type Key = string
type SelectState =
{ Label : string
SelectedKey : Key option
Values : (Key * string) list
Placeholder : (Key * string) option
Validators : SelectValidator list
ValidationSelectState : ValidationState
IsLoading : bool
ValuesFromServer : JS.Promise<(Key * string) list> option }
and SelectValidator = SelectState -> ValidationState
type Key = string
type SelectState =
{ Label : string
SelectedKey : Key option
Values : (Key * string) list
Placeholder : (Key * string) option
Validators : SelectValidator list
ValidationSelectState : ValidationState
IsLoading : bool
ValuesFromServer : JS.Promise<(Key * string) list> option }
and SelectValidator = SelectState -> ValidationState
type Fields =
| Input of InputState
| Select of SelectState
type Key = string
type SelectState =
{ Label : string
SelectedKey : Key option
Values : (Key * string) list
Placeholder : (Key * string) option
Validators : SelectValidator list
ValidationSelectState : ValidationState
IsLoading : bool
ValuesFromServer : JS.Promise<(Key * string) list> option }
and SelectValidator = SelectState -> ValidationState
type Fields =
| Input of InputState
| Select of SelectState
type FormState<'AppMsg> =
{ Fields : (Guid * Fields) list
OnFormMsg : (Msg -> 'AppMsg) option }
Support several fields type
Support actions
type FormState<'AppMsg> =
{ Fields : (Guid * Field) list
OnFormMsg : (Msg -> 'AppMsg) option
ActionsArea : ReactElement }
{
"firstname": "Maxime",
"surname": "Mangel",
"email": "mangel.maxime@email.com",
"favLang": "9"
}
type InputState =
{ // ...
JsonLabel : string option
// ... }
type SelectState =
{ // ...
JsonLabel : string option
// ... }
Support JSON serialization
Since released
FAKE - F# Make
package.json scripts
Documentation
Demo
Interactive preview
Immediate usage
type Point =
{ X : int
Y : int }
static member Decoder =
Decode.decode
(fun x y ->
{ X = x
Y = y } )
|> Decode.required "x" Decode.int
|> Decode.required "y" Decode.int
type Point =
{ X : int
Y : int }
static member Decoder =
Decode.object
(fun get ->
{ X = get.Required.Field "x" Decode.int
Y = get.Required.Field "y" Decode.int } )
Thoth.Json
V1
V2
Fable REPL
V1
V2