formal/form

Types

An invalid or unfinished form. This is either created by the new function, which creates a new empty form, or by the finish function when the validation failed, returning the invalid form.

pub type FormState {
  FormState(
    values: Dict(String, List(String)),
    errors: Dict(String, String),
  )
}

Constructors

  • FormState(
      values: Dict(String, List(String)),
      errors: Dict(String, String),
    )

A collection of validations that decode data from a form into a typed value.

See the module documentation for an overview of how to use this type to validate a form.

pub opaque type FormValidator(output)

Functions

pub fn and(
  previous: fn(a) -> Result(b, String),
  next: fn(b) -> Result(c, String),
) -> fn(a) -> Result(c, String)

Add an additional validation step to the field decoder function.

This function behaves similar to the try function in the standard library gleam/result module.

pub fn bool(input: String) -> Result(Bool, String)

Decode the field value as a bool.

The input value must be “on” to be considered true as this is the value that HTML checkboxes use when checked.

Examples

bool("on")
# -> Ok(True)
bool("true")
# -> Ok(False)
bool("")
# -> Ok(False)
pub fn decoding(
  into constructor: fn(a) -> b,
) -> FormValidator(fn(a) -> b)

Set the constructor that is used to create the success value if the form decodes and validates successfully.

You will want to use a curried constructor function here. The curry* functions in the gleam/function standard library module can be used to help with this.

pub fn field(
  form: FormValidator(fn(a) -> b),
  name: String,
  decoder: fn(String) -> Result(a, String),
) -> FormValidator(b)

Add the next field to be decoded and validated from the form, corresponding to the next argument to the constructor.

pub fn finish(form: FormValidator(a)) -> Result(a, FormState)

Finish the form validation, returning the success value built using the constructor, or the invalid form containing the values and errors, which can be used to render the form again to the user.

pub fn float(input: String) -> Result(Float, String)

Decode the field value as a float.

The input value must have a decimal point.

Examples

float("12.34")
# -> Ok(123)
float("1")
# -> Error("Must be a number with a decimal point")
float("ok")
# -> Error("Must be a number with a decimal point")
pub fn int(input: String) -> Result(Int, String)

Decode the field value as an int.

Examples

int("123")
# -> Ok(123)
int("ok")
# -> Error("Must be a whole number")
pub fn list(
  of decoder: fn(String) -> Result(a, String),
) -> fn(List(String)) -> Result(List(a), String)

Decode all the values for a field as a given type. This is useful with the multifield function when there are multiple inputs with the same name in the form.

Examples

int("123")
# -> Ok(123)
int("ok")
# -> Error("Must be a whole number")
pub fn message(
  result: fn(a) -> Result(b, String),
  message: String,
) -> fn(a) -> Result(b, String)

Set a custom error message for a field decoder, overwriting the previous error message if there is one at this point in the decoder, doing nothing if the decoder is successful.

pub fn multifield(
  form: FormValidator(fn(a) -> b),
  name: String,
  decoder: fn(List(String)) -> Result(a, String),
) -> FormValidator(b)

Add the next field to be decoded and validated from the form, corresponding to the next argument to the constructor.

This function is useful when you have multiple inputs with the same name in the form, and you most likely want to use it with the list decoder function. When there is only a single input with the given name in the form then the field function is more appropriate.

pub fn must_be_accepted(input: Bool) -> Result(Bool, String)

Assert that the bool input is true. This is expected to be used with checkboxes and has an error message that reflects that.

Examples

must_be_accepted(True)
# -> Ok(True)
must_be_accepted(False)
# -> Error("Must be accepted")
pub fn must_be_an_email(input: String) -> Result(String, String)

Assert that the string input looks like an email address.

It could still be an invalid email address even if it looks like one. To validate an email address is valid you will need to send an email to it and ensure your user receives it.

Examples

must_be_an_email("hello@example.com")
# -> Ok("hello@example.com")
must_be_an_email("Something")
# -> Error("Must be an email")
pub fn must_be_greater_float_than(
  minimum: Float,
) -> fn(Float) -> Result(Float, String)

Assert that the float input is greater than the given minimum.

It could still be an invalid email address even if it looks like one. To validate an email address is valid you will need to send an email to it and ensure your user receives it.

Examples

let check = must_be_greater_float_than(3.3)
check(4.1)
# -> Ok(3.3)
let check = must_be_greater_float_than(3.3)
check(2.0)
# -> Error("Must be greater than 3.3")
pub fn must_be_greater_int_than(
  minimum: Int,
) -> fn(Int) -> Result(Int, String)

Assert that the int input is greater than the given minimum.

It could still be an invalid email address even if it looks like one. To validate an email address is valid you will need to send an email to it and ensure your user receives it.

Examples

let check = must_be_greater_int_than(10)
check(12)
# -> Ok(12)
let check = must_be_greater_int_than(10)
check(2)
# -> Error("Must be greater than 10")
pub fn must_be_lesser_float_than(
  maximum: Float,
) -> fn(Float) -> Result(Float, String)

Assert that the float input is greater than the given minimum.

It could still be an invalid email address even if it looks like one. To validate an email address is valid you will need to send an email to it and ensure your user receives it.

Examples

let check = must_be_lesser_float_than(10)
check(12)
# -> Ok(12)
let check = must_be_lesser_float_than(10)
check(2)
# -> Error("Must be less than 10")
pub fn must_be_lesser_int_than(
  maximum: Int,
) -> fn(Int) -> Result(Int, String)

Assert that the int input is greater than the given minimum.

It could still be an invalid email address even if it looks like one. To validate an email address is valid you will need to send an email to it and ensure your user receives it.

Examples

let check = must_be_lesser_int_than(10)
check(12)
# -> Ok(12)
let check = must_be_lesser_int_than(10)
check(2)
# -> Error("Must be less than 10")
pub fn must_be_string_longer_than(
  length: Int,
) -> fn(String) -> Result(String, String)

Assert that the string has at least the given length.

Examples

let check = must_be_string_longer_than(4)
check("hello")
# -> Ok("hello")
let check = must_be_string_longer_than(4)
check("hi")
# -> Error("Must be longer than 2 characters")
pub fn must_equal(
  expected: a,
  because error_message: String,
) -> fn(a) -> Result(a, String)

Assert that the bool input is true. This is expected to be used with checkboxes and has an error message that reflects that.

Examples

let check = must_equal(42, "Must be the answer to everything")
check(42)
# -> Ok(42)
let check = must_equal(42, "Must be the answer to everything")
check(2)
# -> Error("Must be the answer to everything")
pub fn must_not_be_empty(input: String) -> Result(String, String)

Assert that the string input must not be empty, returning an error if it is.

Examples

must_not_be_empty("Hello")
# -> Ok("Hello")
must_not_be_empty("")
# -> Error("Must not be blank")
pub fn new() -> FormState

Create a new empty form.

You likely want to use this when rendering a page containing a new form.

pub fn number(input: String) -> Result(Float, String)

Decode the field value as a float.

The decimal point is optional.

Examples

number("12.34")
# -> Ok(123)
number("1")
# -> Ok(1.0)
number("ok")
# -> Error("Must be a number")
pub fn string(input: String) -> Result(String, String)

Decode the field value as a string.

Examples

string("hello")
# -> Ok("hello")
pub fn with_values(
  form: FormValidator(a),
  values: List(#(String, String)),
) -> FormValidator(a)

Set the values from the form submission to be validated.

HTML forms can have multiple fields with the same name. This function will use the final value with each name, so if you wish to use a different value consider removing the duplicates or using with_values_dict.

pub fn with_values_dict(
  form: FormValidator(a),
  values: Dict(String, List(String)),
) -> FormValidator(a)

Set the values from the form submission to be validated.

Search Document