Validation
As discussed in the Form Introduction chapter, we want to follow Parse don't validate, and for that, we have a nice validation API that goes along very well with our forms.
Most (if not all) form components need a parse
function, which takes in the
value the component usually takes in (i.e. a Text input will take in a String
),
and returns a parsed version of that value, which could be any type. Of course,
validation can fail, so the parse
function actually produces a Result String parsedType
,
where the String
is a (translated) error message. So, all that to say that we
need to give form components a function that looks like this:
parse : rawType -> Result String parsedType
Which is nothing magic! You can use any regular function that will fit that signature.
However, the Form.Validate
module offers a more standard way of doing things,
standard error messages (but they can be customized if you want), and a bunch of
helper functions which are very common to be needed. It operates on an opaque type,
Validator
, which represents the result of parsing something, and it looks
exactly like we might expect:
type Validator output =
Validator (Result Error output)
The only difference being that Error
is actually a function that receives Translators
as input, and returns a translated String
. The reason we don't just have a String
instead of Error
is that the code that calls the validator only needs to provide
Translators
once, and we can use those Translators
only when finally parsing
the data.
The Validator
module also uses a pipeline approach to its API:
parse =
-- We start the `Validator` with `succeed`
Form.Validate.succeed
-- We apply some validations that change the input type
-- `int` turns a `String` input into an `Int`
>> Form.Validate.int
-- Now that we have an `Int`, we can check if it's greater than 0
>> Form.Validate.intGreaterThan 0
-- We can implement our own parsing function!
>> Form.Validate.custom
(\intParameter ->
if intParameter > 100 then
-- If there is a parsing error, we can translate it
-- We could also use `Form.Validate.intLowerThan`
Err (\translators_ -> translators_.t "error.number_too_high")
else if modBy 3 intParameter && modBy 5 intParameter then
Ok "FizzBuzz"
else if modBy 3 intParameter then
Ok "Fizz"
else if modBy 5 intParameter then
Ok "Buzz"
else
String.fromInt intParameter
)
-- Now that we have applied everything we needed, we can validate the data
-- Note that we need to give it some `Translators`
>> Form.Validate.validate translators
Note that we used the >>
pipe instead of |>
. We could have used |>
if we wanted:
parse value =
Form.Validate.succeed value
|> ...