How to submit a form in Elm?

BoumTAC picture BoumTAC · Apr 3, 2016 · Viewed 9k times · Source

It's a really basic question but I didn't find any example.
I have a view like this :

view address model =
  div []
    [ div [] [ text <|"ID : " ++ toString model.id ]
    , form
        []
        [ input [ value model.title ] []
        , textarea [ value model.content ] []
        , button [ onClick address ( SubmitPost model ) ] [ text "Submit" ] // Here is the issue, I want to send my updated model
        ]
    ]

So it display a form with the content inside.
So if I write in my input and textarea to update the content, how do I "catch" my updated model on the onClick event on the button to send it?

Answer

Chad Gilbert picture Chad Gilbert · Apr 3, 2016

The standard way to handle forms in Elm is to trigger updates to your model whenever anything changes on the form. You will typically see some kind of on event attribute attached to each form element.

For your example, you'll want to use on "input" to fire events that update your model with the latest value. But before we can do that, we'll need to create some actions that respond to updates from either field.

type Action
  = SubmitPost
  | UpdateTitle String
  | UpdateContent String

I took the liberty of changing your SubmitPost Model action to just SubmitPost. Since we're changing your code to always be up to date, you don't need anything other than the action SubmitPost to trigger an event that does the submission.

Now that you have the additional actions, you'll need to handle them in the update function:

update action model =
  case action of
    UpdateTitle s -> 
      ({ model | title = s }, Effects.none)
    UpdateContent s -> 
      ({ model | content = s }, Effects.none)
    ...

We can now add the on attributes onto your text fields to trigger updates whenever anything changes. "input" is the event that browsers will fire when text content changes, and it gives you more coverage than just watching for something like keypress events.

view address model =
  div []
    [ div [] [ text <| "ID : " ++ toString model.id ]
    , form
      []
      [ input
        [ value model.title
        , on "input" targetValue (Signal.message address << UpdateTitle)
        ]
        []
      , textarea
        [ value model.content
        , on "input" targetValue (Signal.message address << UpdateContent)
        ]
        []
      , button [ onClick address SubmitPost ] [ text "Submit" ]
      ]
    ]

The targetValue decoder is a Json Decoder which inspects the javascript event that was fired, drilling down to the event.target.value field inside the javascript object, which contains the full value of the text field.