# formal
Type safe HTML form decoding and validation!
[](https://hex.pm/packages/formal)
[](https://hexdocs.pm/formal/)
```sh
gleam add formal@3
```
```gleam
import formal/form
// Define a type that is to be decoded from the form data
pub type SignUp {
SignUp(email: String, password: String)
}
/// A form that decodes the `Signup` value.
fn signup_form() -> Form(Signup) {
form.new({
use email <- form.field("email", {
form.parse_email
})
use password <- form.field("password", {
form.parse_string
|> form.check_string_length_more_than(7)
})
use _ <- form.field("confirm", {
form.parse_string
|> form.check_confirms(password)
})
form.success(SignUp(email: email, password: password))
})
}
// This function takes the list of key-value string pairs that a HTML form
// produces. It then decodes the form data into a SignUp value, ensuring that
// all the fields are present and valid.
//
pub fn handle_form_submission(values: List(#(String, String))) {
let result =
signup_form()
|> form.add_values(values)
|> form.run
case result {
Ok(data) -> {
// Do something with the SignUp value here
}
Error(form) -> {
// Re-render the form with the error messages
}
}
}
```
## Example applications
- [Backend example with Wisp](https://github.com/lpil/formal/tree/main/examples/backend-with-wisp)
- [Frontend example with Lustre](https://github.com/lpil/formal/tree/main/examples/frontend-with-lustre)
## Parsing variants example
Sometime you may have a complex form that could be in one of several formats,
and you wish to parse the different variants of this form into a single Gleam
custom type. This can be done by having a hidden input on the forms to indicate
which variant it is, and then pattern matching on the result.
```gleam
/// This variant indicates which variant it is
pub type VisitorKind {
GuestKind
UserKind
}
/// A parser for that type
fn parse_visitor_kind() {
form.parse(fn(values) {
case values {
["guest", ..] -> Ok(GuestKind)
["user", ..] -> Ok(UserKind)
_ -> Error(#(WibbleForm, "must be guest or user"))
}
})
}
/// The custom type to decode the form into
type VisitorForm {
GuestForm(email: String)
UserForm(username: String)
}
/// In the form function the kind is parsed first, and then depending on which
/// variant it is different form logic follows.
fn wibble_form() {
form.new({
use variant <- form.field("kind", parse_visitor_kind())
case variant {
GuestKind -> {
use email <- form.field("email", form.parse_email)
form.success(GuestForm(email:))
}
UserKind -> {
use wobble <- form.field("username", form.parse_string)
form.success(UserForm(username:))
}
}
})
}
```
Further documentation can be found at <https://hexdocs.pm/formal>.