Fmlib form (elm for ocaml)

Very beginner question here:

This code makes the barest minimum form, now to add more text boxes to the form, how would I do this in the most generic way possible, so that the same function can be used to update many text boxes.

It is this function I don’t want to repeat 10 times for multiple text boxes: “let first_name str = FirstName str in”

and in the update function I am somewhat unsure exactly what I am doing. If I add a last name text box with a similar update function for the new field i get a compiler error.

(* file: form.ml *)
open Fmlib_browser

type state = 
    {
        first_name: string;
    }

let init: state =
    {
        first_name = "";
    }

type msg =
    | FirstName of string
 
let view state =
    let open Html in
    let open Attribute in
    let first_name str = FirstName str in
    let section attrs nodes = node "section" attrs nodes in
    let form attrs nodes = node "form" attrs nodes in
    div [class_ "container"] [
       section [id "main"] [
            form [] [
                section [id "details"] [
                    div [class_ "grid"] [
                        label [] [text "First Name"; input [
                            attribute "type" "text"
                            ; value state.first_name
                            ; on_input first_name] []]
                    ]
                ]
            ]
            ; p [] [
              text "first name: "
            ; text (state.first_name) ]
        ]
    ]
    
let update state = function
    | FirstName first_name ->
        {first_name}

let _ =
    sandbox         (* very simple applications are sandbox applications *)
        init           (* initial state *)
        view        (* view function *)
        update      (* update function *)

Kindness
David

I’m not sure what you mean by wanting a generic function, but basically in your case you need one msg per input. So that you can pattern match accordingly in your update function.

To keep things clean, you can group a bunch of related msgs together, like so:

type form_msg =
  | First_name of string
  | Last_name of string

type msg =
  | Form_msg of form_msg
  | No_op

I added the No_op msg for demonstration purposes. Here it is all together:

(* file: form.ml *)
open Fmlib_browser

type state =
  { first_name : string
  ; last_name : string
  }

let init : state = { first_name = ""; last_name = "" }

type form_msg =
  | First_name of string
  | Last_name of string

type msg =
  | Form_msg of form_msg
  | No_op
[@@warning "-37"]

let view (state : state) =
  let open Html in
  let open Attribute in
  let first_name str = Form_msg (First_name str) in
  let last_name str = Form_msg (Last_name str) in
  let section attrs nodes = node "section" attrs nodes in
  let form attrs nodes = node "form" attrs nodes in
  div
    [ class_ "container" ]
    [ section
        [ id "main" ]
        [ form
            []
            [ section
                [ id "details" ]
                [ div
                    [ class_ "grid" ]
                    [ label
                        []
                        [ text "First Name"
                        ; input
                            [ attribute "type" "text"
                            ; value state.first_name
                            ; on_input first_name
                            ]
                            []
                        ]
                    ]
                ; div
                    [ class_ "grid" ]
                    [ label
                        []
                        [ text "Last Name"
                        ; input
                            [ attribute "type" "text"
                            ; value state.last_name
                            ; on_input last_name
                            ]
                            []
                        ]
                    ]
                ]
            ]
        ; p [] [ text "first name: "; text state.first_name ]
        ]
    ]
;;

let update (state : state) = function
  | No_op -> state
  | Form_msg msg ->
    (match msg with
     | First_name str -> { state with first_name = str }
     | Last_name str -> { state with last_name = str })
;;

let _ =
  sandbox (* very simple applications are sandbox applications *)
    init (* initial state *)
    view (* view function *)
    update (* update function *)
;;

I have a few fmlib examples that you could find useful here: tea-playground/ocaml/fmlib at master · benjamin-thomas/tea-playground · GitHub

1 Like

Thank you for your help.

I am clearer. It seems quite simple and very explicit, that is a good thing. New synapses forming.

Regards
David

I am really enjoying fmlib and ocaml, thanks for the help and what a great project.

It’s not hard to get something deployed.

Thanks
David

2 Likes