Live form validation and fields state

Hello, I’m having quite some problems understanding and using elm-form (https://github.com/etaque/elm-form).
The problem is that I’m not really sure how to handle the state of the fields and how elm-form functions to generate form elements work.

Here are my models:

type alias Model =
  { form : Form CustomError User
  , userMaybe : Maybe User }

type alias User =
  { type_: UserType}

type UserType
  = Designer
  | Supplier

So far so good, what I really want are 2 radio buttons, one for Designer and one for Supplier.
From what I could understand from documentation, I must write validation functions also to handle the String -> UserType conversion.

validate : Validation CustomError User
validate =
  map2 User
    (field "user[type]" validateUserType)
    
validateUserType : Validation CustomError UserType
validateUserType =
  customValidation 
    string
    (\s ->
      case s of
        "designer" ->
          Ok Designer
        "supplier" ->
          Ok Supplier
        _ ->
          Err (customError InvalidUserType)
    )

My understanding here is that these functions validate a string into a UserType, which is very smart and useful.
My doubts start more or less here: first of all the initial value of the form

initialFields : List ( String, Field )
initialFields =
  [ ("user[type]", Field.string "designer") ]

It’s a rails app, that notation will be parsed as {user: {type: “designer”}} which is what I need. My understanding here is that I must use what would become the name attribute in HTML.
And now I become superconfused:

update : Message -> Model -> (Model, Cmd Message)
update message ({ form } as model) =
  case message of
    FormMsg formMsg ->
      case ( formMsg, Form.getOutput form ) of
        _ ->
          ( { model | form = Form.update validate formMsg form }, Cmd.none )

I removed some code but the case is correct, the Form.update is the relevant part.

view : Model -> Html Message
view {form, userMaybe} =
  div []
    [ Html.map FormMsg (formView form)
    , case userMaybe of
      Just user ->
        p [ class "alert alert-success" ] [ text (toString user) ]

      Nothing ->
        text ""
    ]

formView : Form CustomError User -> Html Form.Msg
formView form =
  div [class "form-group"] [
    div [class "custom-control custom-radio"] [
      Input.radioInput "designer" (Form.getFieldAsString "user[type]" form) [class "custom-control-input", name "user[type]"],
      label [class "custom-control-label"] [text "I need something manufactured"]
    ],
    div [class "custom-control custom-radio"] [
      Input.radioInput "supplier" (Form.getFieldAsString "user[type]" form) [class "custom-control-input", name "user[type]"],
      label [class "custom-control-label"] [text "I can supply services"]
    ]
  ]

Here I lost it. I guess that Form.getFieldAsString gets some state from the form. If I Debug.log it I have:

this: { path = "user[type]", value = Just "designer", error = Nothing, liveError = Nothing, isDirty = False, isChanged = False, hasFocus = False }

which is apparently ok.
But I can’t switch between the radio buttons, the first one (the one I set in the initial state) is always checked and I don’t understand why. Did I forget an attribute or something? It’s just weird that the result is not some error but it just plainly doesn’t work. And I’m pretty lost, I’m a newbie and I’m not entirely sure which steps to take to debug this, and if all the things I think I understand are correct.

Can someone shed a light? Sorry for the very long post, a bit frustrated.

Cheers, ngw

@ngw,

Check out this Ellie: https://ellie-app.com/hhZXhYR78a1/1

This is essentially your code except without the CustomError and I used a “map” in “validate” instead of “map2” because User only takes one argument, not two (but the compiler caught that).

Are you sure you couldn’t switch between radio buttons? Could it just be that the radio buttons weren’t placed inside the label (so you can click the text instead of just the radio)?

I hope this helps.

Iain

thanks a lot, it really helped me to sort this out, and the code is also much better than mine (surprise! :D)
There’s still a problem though that I can’t figure out, the radio is not checked (in your Ellie too).
If I look at the code I’m not sure what that .path is suppsed to be

ngw

So, state.path is the name you’re passing to getFieldAsString: “user[type]”, so really adding the explicit name "user[type]" shouldn’t change anything.

Also, for me the radio is initially checked on “I need something manufactured” in both Chrome and Firefox.

It does not show the p.alert.alert-success until you interact with the form. I believe what is happening there is Form.getOutput will return Nothing until it runs your validate function at least once. You can cause this to happen by either changing a field value or by passing a Form.Msg Validate message through update.