Validating input in Elm

James Carlson
3 min readApr 10, 2018

--

In this post I’ll describe a simple method for validating input in Elm. I’m using it in a voter engagement app I am working on, and I’ll describe it in that context. The app’s model has a voter field, and a voter is a record:

{- LISTING 1 -}type alias Voter =
{ firstName : String
, lastName : String
, email : String
, zipcode : String
, birthday : String
, campaign : String
, points : Int
, registered : Bool
}

A voter who signs up to use the app enters the first five fields of Voterand optionally the sixth. The validateInput method in module Validation checks the first five fields as described in LISTING 3 below. The validateInput is called like this:

{- LISTING 2 -}signUpVoter model =
let
errorList =
validateInput model
in
if errorList == [] then
( { model | state = VotersSubmitted, errors = [] },
Request.doRequest
<| RequestData.signupParameters model )
else
( { model | state = InvalidInput, errors = errorList },
Cmd.none )

The output of validateInput model is a list of errors — just a list of strings. If the list is empty, we proceed with normal output. If it is nonempty, we set the model variable message to this list. The app’s view function takes care of displaying the message. (For an explanation of Request.doRequest , see this article.)

Defining the validateInput function

The validateInput function is a pipeline of functions with signature like the one below.

validatePassword : Model -> List String -> List String

The function validatePasswordtakes the model and a list of errors as input. It then examines the model. If it finds an error, it adds it to the head of the list of errors and returns the updated list. If it finds no error, it passes the list of error on unchanged.

The validateInputis a pipeline of functions that examine the model and add an error to the error list if necessary. The return value is therefore a list of all the errors discovered.

Take a look at LISTING 3 to see how the validation functions are defined. This “pipeline” approach is not rocket science, but it is a solution that is easy to maintain. To add another validation, define it so that it has the signature

Model -> List String -> List String

and add it to the pipeline. That’s all there is to it!

Here is the complete listing for the error-checking module.

{- LISTING 3 -}module Validation exposing (validateInput)import Types exposing (Model, Voter)validateInput : Model -> List String
validateInput model =
validateFirstName model.voter []
|> validateLastName model.voter
|> validateEmail model.voter
|> validatezipcode model.voter
|> validateBirthday model.voter
|> validatePassword model
validatePassword : Model -> List String -> List String
validatePassword model errorList =
if model.password == model.passwordAgain then
errorList
else
"Passwords do not match." :: errorList
validateBirthday : Voter -> List String -> List String
validateBirthday voter errorList =
if (voter.birthday |> String.split ("/") |> List.length) == 3 then
errorList
else
"Date does not have three parts, as it should: 10/5/1999" :: errorList
validateFirstName : Voter -> List String -> List String
validateFirstName voter errorList =
if voter.firstName == "" then
"First name must be nonempty" :: errorList
else
errorList
validateLastName : Voter -> List String -> List String
validateLastName voter errorList =
if voter.lastName == "" then
"Last name must be nonempty" :: errorList
else
errorList
validateEmail : Voter -> List String -> List String
validateEmail voter errorList =
if voter.email == "" then
errorList ++ [ "Email address can't be empty" ]
else if (voter.email |> String.split ("@") |> List.length) /= 2 then
"Invalid email address" :: errorList
else
errorList
validatezipcode : Voter -> List String -> List String
validatezipcode voter errorList =
if (voter.zipcode |> String.length) /= 5 then
"zipcode must have five digits" :: errorList
else
errorList

Note. I would like to thank @akoppela for suggesting a more elegant way to add an element to a list.

--

--

Responses (2)