Skip to content

lpil/formal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

63 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

formal

Type safe HTML form decoding and validation!

Package Version Hex Docs

gleam add formal@3
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

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.

/// 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.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages