This module provides a scalable way to validate a form by combining primitive validators.
For example, let's assume a form having two inputs as follows.
type alias Form =
{ sampleInput : Maybe Int
, anotherInput : Maybe String
}The first step is to define a validator for each input.
import Regex exposing (regex)
type SampleError
= SampleBoundError
| SampleRequiredError
sampleValidator : Validator (Maybe Int) SampleError
sampleValidator =
required SampleRequiredError <|
concat
[ minBound SampleBoundError 10
, maxBound SampleBoundError 20
]
errors sampleValidator (Just 15)
--> []
errors sampleValidator (Just 30)
--> [ SampleBoundError ]
errors sampleValidator Nothing
--> [ SampleRequiredError ]
isValid sampleValidator (Just 15)
--> True
isValid sampleValidator (Just 30)
--> False
type AnotherError
= AnotherLengthError
| AnotherPatternError
anotherValidator : Validator (Maybe String) AnotherError
anotherValidator =
optional <|
concat
[ maxLength AnotherLengthError 20
, pattern AnotherPatternError <| regex "^(http://|https://)"
]
errors anotherValidator Nothing
--> []
errors anotherValidator (Just "foo")
--> [ AnotherPatternError ]
errors anotherValidator (Just "https://foo")
--> []
errors anotherValidator (Just "https://tooooooooooolong")
--> [ AnotherLengthError ]
errors anotherValidator (Just "ftp://tooooooooooolong")
--> [ AnotherLengthError, AnotherPatternError ]
isValid anotherValidator Nothing
--> True
isValid anotherValidator (Just "foo")
--> FalseThe next step is combining these validators to create a validator for the entire form.
type FormError
= SampleError SampleError
| AnotherError AnotherError
formValidator : Validator Form FormError
formValidator =
concat
[ liftMap SampleError .sampleInput sampleValidator
, liftMap AnotherError .anotherInput anotherValidator
]
errors formValidator
{ sampleInput = Just 15
, anotherInput = Just "https://foo"
}
--> []
errors formValidator
{ sampleInput = Nothing
, anotherInput = Nothing
}
--> [ SampleError SampleRequiredError ]
errors formValidator
{ sampleInput = Nothing
, anotherInput = Just "foo"
}
--> [ SampleError SampleRequiredError
--> , AnotherError AnotherPatternError
--> ]
displayFormError : FormError -> String
displayFormError err =
case err of
SampleError SampleRequiredError ->
"Sample Input cannot be empty"
SampleError SampleBoundError ->
"Sample Input is out of bounds"
AnotherError AnotherLengthError ->
"Length of Another Input is toooo long"
AnotherError AnotherPatternError ->
"Another Input must begin with `http://` or `https://`"
map displayFormError <|
errors formValidator
{ sampleInput = Nothing
, anotherInput = Nothing
}
--> [ "Sample Input cannot be empty" ]Run validator to a target value and returns all validation errors.
The isValid only checks if a target value is valid or not.
A constructor for Validator which always results to valid.
isValid succeed "foo"
--> True
isValid succeed <| Just 34
--> True
errors (required "Required error" succeed) <| Nothing
--> [ "Required error" ]A constructor for Validator providing minimum bound.
errors (minBound "Too small" 10) 2
--> [ "Too small" ]A constructor for Validator providing maximum bound.
errors (maxBound "Too large" 100) 200
--> [ "Too large" ]A constructor for Validator providing maximum length.
errors (maxLength "Too long" 10) "tooooooooo long"
--> [ "Too long" ]A constructor for Validator providing minimum length.
errors (minLength "Too short" 10) "short"
--> [ "Too short" ]A constructor for Validator from a regular expression.
import Regex exposing (regex)
errors (pattern "Pattern error" (regex "^foo")) "foobar"
--> []
errors (pattern "Pattern error" (regex "^foo")) "barfoo"
--> [ "Pattern error" ]A constructor for Validator from a function.
errors (custom (\n -> if n < 10 then Just "Too small" else Nothing)) 8
--> [ "Too small" ]Concatnate list of validators.
import Regex exposing (regex)
errors (concat [ minBound "Too small" 10, maxBound "Too large" 100 ]) 8
--> [ "Too small" ]
errors (concat [ minBound "Too small" 10, maxBound "Too large" 100 ]) 20
--> []
errors (concat [ minLength "Too short" 10, pattern "Does not match pattern" (regex "^foo") ]) "bar"
--> [ "Too short", "Does not match pattern" ]Combine two validators on OR condition.
import Regex exposing (regex)
errors (minLength "Too short" 10 ||. pattern "Does not match pattern" (regex "^foo")) "foobar"
--> []
errors (minLength "Too short" 10 ||. pattern "Does not match pattern" (regex "^foo")) "enough long"
--> []
errors (minLength "Too short" 10 ||. pattern "Does not match pattern" (regex "^foo")) "short"
--> [ "Does not match pattern" ]A convenient wrapper for validating required values.
errors (required "Cannot be empty" <| minBound "Too small" 10) Nothing
--> [ "Cannot be empty" ]
errors (required "Cannot be empty" <| minBound "Too small" 10) <| Just 100
--> []
errors (required "Cannot be empty" <| minBound "Too small" 10) <| Just 2
--> [ "Too small" ]A convenient wrapper for validating optional values.
errors (optional <| minLength "Too small" 10) Nothing
--> []
errors (optional <| minLength "Too small" 10) <| Just "enough long"
--> []
errors (optional <| minLength "Too small" 10) <| Just "short"
--> [ "Too small" ]Only checks validity if a condition is True.
import Regex exposing (regex)
checkPrefix : Validator String String
checkPrefix = pattern "Incorrect format" (regex "^foo")
errors (when (\str -> String.length str > 2) checkPrefix) "ba"
--> []
errors (when (\str -> String.length str > 2) checkPrefix) "bar"
--> [ "Incorrect format" ]Only checks validity unless a condition is True.
import Regex exposing (regex)
checkPrefix : Validator String String
checkPrefix = pattern "Incorrect format" (regex "^foo")
errors (unless (\str -> String.length str < 3) checkPrefix) "ba"
--> []
errors (unless (\str -> String.length str < 3) checkPrefix) "bar"
--> [ "Incorrect format" ]Convert err type.
lift is mainly used for accessing sub model of target value.
errors (lift .str <| minLength "Too short" 10) { str = "foo", int = 5 }
--> [ "Too short" ]liftMap can convert a validator by lift and map at one time for convenience.
An opaque type representing validator for value of type
a.