hecrj / composable-form / Form.Base

Build composable forms with your own custom fields.

This is the base of the composable-form package. It implements a composable Form type that is not tied to any particular field.

In order to understand this module, you should be familar with the basic Form module first.

Definition


type Form values output field

A Form that can contain any type of field.

Custom fields

Say you need to use a type of field that is not implemented in the the basic Form module. The recommended way of doing this is to start your own Form module using field and custom to define new types of fields.

For instance, you could start your own MyProject.Form module like this:

import Form.Base as Base

type alias Form values output =
    Base.Form values output (Field values)

type Field values
    = None

succeed : output -> Form values output
succeed =
    Base.succeed

-- Other useful operations you will probably want to use,
-- like append, andThen...

Notice that we could avoid redefining succeed, append, and others, but that would force us to import Form.Base every time we needed to use those operations with our brand new form.

field : { isEmpty : input -> Basics.Bool } -> (Form.Field.Field attributes input values -> field) -> FieldConfig attributes input values output -> Form values output field

Create functions that build forms which contain a single field with an API that is similar to the basic Form module.

This function is meant to be partially applied, providing only the two first parameters to obtain a function that expects the configuration for a particular type of field. See FieldConfig.

For this, you only need to provide:

For example, Form.textField could be implemented like this:

textField :
    { parser : String -> Result String output
    , value : values -> String
    , update : String -> values -> values
    , attributes : TextField.Attributes
    }
    -> Form values output
textField =
    Base.field { isEmpty = String.isEmpty } (Text TextRaw)

Notice how the configuration record in textField is a FieldConfig.

Note: You can use TextField.form, SelectField.form, and others to build fields that are already present in Form.


type alias FieldConfig attrs input values output =
{ parser : input -> Result String output
, value : values -> input
, update : input -> values -> values
, error : values -> Maybe String
, attributes : attrs 
}

Most form fields require configuration! FieldConfig allows you to specify how a concrete field is validated and updated, alongside its attributes:

custom : (values -> CustomField output field) -> Form values output field

Create a custom field with total freedom.

You only need to provide a function that given some values produces a FilledField.

You can check the custom fields example for some inspiration.


type alias CustomField output field =
{ state : field
, result : Result ( Form.Error.Error
, List Form.Error.Error ) output
, isEmpty : Basics.Bool 
}

Represents a custom field on a form that has been filled with values.

It contains:

Composition

succeed : output -> Form values output field

Like Form.succeed but not tied to a particular type of field.

append : Form values a field -> Form values (a -> b) field -> Form values b field

Like Form.append but not tied to a particular type of field.

andThen : (a -> Form values b field) -> Form values a field -> Form values b field

Like Form.andThen but not tied to a particular type of field.

optional : Form values output field -> Form values (Maybe output) field

Like Form.optional but not tied to a particular type of field.

disable : Form values output field -> Form values output field

Like Form.disable but not tied to a particular type of field.

meta : (values -> Form values output field) -> Form values output field

Like Form.meta but not tied to a particular type of field.

Mapping

map : (a -> b) -> Form values a field -> Form values b field

Like Form.map but not tied to a particular type of field.

mapValues : (a -> b) -> Form b output field -> Form a output field

Apply a function to the input values of the form.

mapField : (a -> b) -> Form values output a -> Form values output b

Apply a function to each form field.

Output


type alias FilledForm output field =
{ fields : List (FilledField field)
, result : Result ( Form.Error.Error
, List Form.Error.Error ) output
, isEmpty : Basics.Bool 
}

Represents a filled form.

You can obtain this by using fill.


type alias FilledField field =
{ state : field
, error : Maybe Form.Error.Error
, isDisabled : Basics.Bool 
}

Represents a filled field.

fill : Form values output field -> values -> FilledForm output field

Like Form.fill but not tied to a particular type of field.