hecrj/composable-form/1.0.1

NOTE: this package is not compatible with Elm 0.19.1

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 copy the source code of the basic Form module into your own MyProject.Form module, and then use field and custom to define new types of fields.

While copy-pasting code might sound like a bad idea, keep in mind that the basic Form module does not contain much code at all! It is just a bunch of one-liners that delegate to this base module. It is the perfect template to build your own form.

If that still does not convince you, 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
    = 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.

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

Create functions that build forms that 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:

  • A function that given the input of the field tells whether it is empty or not.
  • A function that maps a generic Field to your own specific field type.

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

textField :
    { parser : String -> Result String output
    , value : values -> Value String
    , update : Value 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 -> Value input
, update : Value input -> values -> values
, attributes : attrs
}

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

  • parser must be a function that validates the input of the field and produces a Result of either:
    • the correct output
    • a String describing a problem
  • value defines how the Value of the field is obtained from the form values
  • update defines how the current form values should be updated with a new field Value
  • attributes represent the attributes of the field
custom : (values -> FilledField output custom) -> Form values output custom

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 FilledField output field =
{ field : field
, result : Result ( Error, List Error ) output
, isEmpty : Bool
}

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

It contains:

  • a field
  • the result of the field
  • whether the field is empty or not

Composition

succeed : output -> Form values output custom

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

Form values a custom
-> Form values (a -> b) custom
-> Form values b custom

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

(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 custom -> Form values (Maybe output) custom

Like Form.optional 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.

Output

type alias FilledForm field output =
{ fields : List ( field, Maybe Error )
, result : Result ( Error, List Error ) output
, isEmpty : Bool
}

Represents a filled form.

You can obtain this by using fill.

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

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