Convenience functions for validating data.
import Validate exposing (Validator, ifBlank, ifNotInt, validate)
type Field = Name | Email | Age
type alias Model = { name : String, email : String, age : String }
modelValidator : Validator String Model
modelValidator =
Validate.all
[ ifBlank .name "Please enter a name."
, Validate.firstError
[ ifBlank .email "Please enter an email address."
, ifInvalidEmail .email "This is not a valid email address."
]
, ifNotInt .age "Age must be a whole number."
]
validate modelValidator { name = "Sam", email = "blah", age = "abc" }
--> Err [ "This is not a valid email address.", "Age must be a whole number." ]
A Validator
contains a function which takes a subject and returns a list
of errors describing anything invalid about that subject.
Pass it to validate
to get the list of errors.
An empty error list means the subject was valid.
Guarantees that a subject has been run through a Validator which passed.
The only way to obtain one of these is to call validate
, so if you have a
Valid Form
, you can be certain the wrapped Form
value has been run through
a Validator
that returned in no errors.
Once you have a Valid Form
, you can unwrap the validated Form
by calling
fromValid
.
This lets you write functions like sendToServer : Valid Form -> ...
which make it impossible to forget to run the form through a Validator
before sending it off to the server!
validate : Validator error subject -> subject -> Result (List error) (Valid subject)
Return an error if the given predicate returns True
for the given
subject.
import Validate exposing (Validator, ifBlank, ifNotInt, validate)
type Field = Name | Email | Age
type alias Model = { name : String, email : String, age : String }
modelValidator : Validator ( Field, String ) Model
modelValidator =
Validate.all
[ ifBlank .name ( Name, "Please enter a name." )
, ifBlank .email ( Email, "Please enter an email address." )
, ifNotInt .age ( Age, "Age must be a whole number." )
]
validate modelValidator { name = "Sam", email = "", age = "abc" }
--> Err [ ( Email, "Please enter an email address." ), ( Age, "Age must be a whole number." ) ]
fromValid : Valid subject -> subject
Extract the wrapped Valid value.
ifBlank : (subject -> String) -> error -> Validator error subject
Return an error if the given String
is empty, or if it contains only
spaces, tabs, newlines, or carriage returns.
import Validate exposing (Validator, ifBlank)
modelValidator : Validator Model String
modelValidator =
Validate.all
[ ifBlank .name "Please enter a name."
, ifBlank .email "Please enter an email address."
]
ifNotInt : (subject -> String) -> (String -> error) -> Validator error subject
Return an error if the given String
cannot be parsed as an Int
.
import Validate exposing (Validator, ifNotInt)
modelValidator : Validator Model String
modelValidator =
Validate.all
[ ifNotInt .followers (\_ -> "Please enter a whole number for followers.")
, ifNotInt .stars (\stars -> "Stars was \"" ++ stars ++ "\", but it needs to be a whole number.")"
]
ifNotFloat : (subject -> String) -> error -> Validator error subject
Return an error if a String
cannot be parsed as an Float
.
ifEmptyList : (subject -> List a) -> error -> Validator error subject
Return an error if a List
is empty.
ifEmptyDict : (subject -> Dict comparable v) -> error -> Validator error subject
Return an error if a Dict
is empty.
ifEmptySet : (subject -> Set comparable) -> error -> Validator error subject
Return an error if a Set
is empty.
ifNothing : (subject -> Maybe a) -> error -> Validator error subject
Return an error if a Maybe
is Nothing
.
ifInvalidEmail : (subject -> String) -> (String -> error) -> Validator error subject
Return an error if an email address is malformed.
import Validate exposing (Validator, ifBlank, ifNotInt)
modelValidator : Validator Model String
modelValidator =
Validate.all
[ ifInvalidEmail .primaryEmail (\_ -> "Please enter a valid primary email address.")
, ifInvalidEmail .superSecretEmail (\email -> "Unfortunately, \"" ++ email ++ "\" is not a valid Super Secret Email Address.")
]
ifTrue : (subject -> Basics.Bool) -> error -> Validator error subject
Return an error if a predicate returns True
for the given
subject.
import Validate exposing (Validator, ifTrue)
modelValidator : Validator Model String
modelValidator =
ifTrue (\model -> countSelected model < 2)
"Please select at least two."
ifFalse : (subject -> Basics.Bool) -> error -> Validator error subject
Return an error if a predicate returns False
for the given
subject.
import Validate exposing (Validator, ifFalse)
modelValidator : Validator Model String
modelValidator =
ifFalse (\model -> countSelected model >= 2)
"Please select at least two."
fromErrors : (subject -> List error) -> Validator error subject
Create a custom validator, by providing a function that returns a list of errors given a subject.
import Validate exposing (Validator, fromErrors)
modelValidator : Validator Model String
modelValidator =
fromErrors modelToErrors
modelToErrors : Model -> List String
modelToErrors model =
let
usernameLength =
String.length model.username
in
if usernameLength < minUsernameChars then
[ "Username not long enough" ]
else if usernameLength > maxUsernameChars then
[ "Username too long" ]
else
[]
all : List (Validator error subject) -> Validator error subject
Run each of the given validators, in order, and return their concatenated error lists.
import Validate exposing (Validator, ifBlank, ifNotInt)
modelValidator : Validator Model String
modelValidator =
Validate.all
[ ifBlank .name "Please enter a name."
, ifBlank .email "Please enter an email address."
, ifNotInt .age "Age must be a whole number."
]
any : List (Validator error subject) -> subject -> Basics.Bool
Return True
if none of the given validators returns any errors for the given
subject, and False
if any validator returns one or more errors.
firstError : List (Validator error subject) -> Validator error subject
Run each of the given validators, in order, stopping after the first error
and returning it. If no errors are encountered, return Nothing
.
import Validate exposing (Validator, ifBlank, ifInvalidEmail, ifNotInt)
type alias Model =
{ email : String, age : String }
modelValidator : Validator String Model
modelValidator =
Validate.all
[ Validate.firstError
[ ifBlank .email "Please enter an email address."
, ifInvalidEmail .email "This is not a valid email address."
]
, ifNotInt .age "Age must be a whole number."
]
validate modelValidator { email = " ", age = "5" }
--> Err [ "Please enter an email address." ]
validate modelValidator { email = "blah", age = "5" }
--> Err [ "This is not a valid email address." ]
validate modelValidator { email = "foo@bar.com", age = "5" }
--> Ok (Valid { email = "foo@bar.com", age = "5" })
isBlank : String -> Basics.Bool
Returns True
if the given string is nothing but whitespace.
ifBlank
uses this under the hood.
isInt : String -> Basics.Bool
Returns True
if String.toInt
on the given string returns an Ok
.
ifNotInt
uses this under the hood.
isFloat : String -> Basics.Bool
Returns True
if String.toFloat
on the given string returns an Ok
.
ifNotFloat
uses this under the hood.
isValidEmail : String -> Basics.Bool
Returns True
if the email is valid.
ifInvalidEmail
uses this under the hood.