maca/form-toolkit/1.0.1

NOTE: the latest version is 2.0.0

FormToolkit.Field

Provides types and functions to create form fields of various types, set their attributes, update, and render them.

Field

type Field id
= Field (Node id)

Represents a form field element, which can be an individual field or a group of fields.

This element may consist of individual input fields (such as text, email, or password), groups of fields, or repeatable fields.

The type parameter id corresponds to an optional identifier used for referencing a specific field while decoding or updating attributes, and the type parameter val refers to a value of an arbitrary type.

Fields are preferably identified by a custom type but can also be identified by a String.

type Msg id
= InputChanged
(Maybe id)
Path
Value
{ selectionStart : Int
, selectionEnd : Int
}
| OnCheck (Maybe id) Path Bool
| InputFocused (Maybe id) Path
| InputBlured (Maybe id) Path
| InputsAdded (Maybe id) Path
| InputsRemoved (Maybe id) Path

A message generated through interaction with an input.

update : Msg id -> Field id -> Field id

Updates a form by passing a decoder to validate and produce a result, and a Msg to reflect user interactions.

toHtml : (Msg id -> msg) -> Field id -> Html msg

Renders the form.

type UpdateMsg
    = FieldsUpdated Msg


view : Html UpdateMsg
view =
    group []
        [ text [ label "First Name" ]
        , text [ label "Last Name" ]
        ]
        |> toHtml FieldsUpdated

Field types

text : List (Attribute id val) -> Field id

Builds a text input field.

options can be provided to construct a datalist for autocomplete suggestions.

usernameField : Field id
usernameField =
    text
        [ label "Username"
        , placeholder "Enter your username"
        ]
textarea : List (Attribute id val) -> Field id

Builds a Textarea input field.

commentsField : Field id
commentsField =
    textarea
        [ label "Comments"
        , autogrow True
        , placeholder "Enter your comments here"
        ]
email : List (Attribute id val) -> Field id

Builds an email input field.

emailField : Field id
emailField =
    email [ label "Email", required True ]
password : List (Attribute id val) -> Field id

Builds a password input field.

passwordField : Field id
passwordField =
    password [ label "Password", required True ]
strictAutocomplete : List (Attribute id val) -> Field id

Builds a text input field with strict autocomplete. If the user input doesn't match a provided option, the input value will be blank. The value can be of any type.

For non-strict autocomplete, use text with the options attribute. For string value options, see stringOptions.

strictAutocomplete
    [ label "Language"
    , stringOptions [ "Español", "English", "Deutsch" ]
    ]
int : List (Attribute id val) -> Field id

Builds an integer input field.

ageField : Field id
ageField =
    int
        [ label "Age"
        , min (Value.int 0)
        , max (Value.int 120)
        ]
float : List (Attribute id val) -> Field id

Builds a floating-point number input field.

priceField : Field id
priceField =
    float [ label "Price", min (Value.float 0.0) ]
date : List (Attribute id val) -> Field id

Builds a date input field.

birthdateField : Field id
birthdateField =
    date [ label "Birthdate", required True ]
month : List (Attribute id val) -> Field id

Builds a month input field.

monthField : Field id
monthField =
    month [ label "Expiry Month" ]
datetime : List (Attribute id val) -> Field id

Builds a datetime-local input field.

meetingTimeField : Field id
meetingTimeField =
    datetime [ label "Meeting Time", required True ]
select : List (Attribute id val) -> Field id

Builds a select input field (dropdown).

langSelect : Field id
langSelect =
    select
        [ label "Language"
        , stringOptions [ "Español", "English", "Deutsch" ]
        ]
radio : List (Attribute id val) -> Field id

Builds a radio button input field.

lightOnField : Field id
lightOnField =
    radio
        [ label "Light is"
        , options
            [ ( "On", Value.bool True )
            , ( "Off", Value.bool False )
            ]
        ]
checkbox : List (Attribute id val) -> Field id

Builds a checkbox input field.

consentField : Field id
consentField =
    checkbox [ label "Subscribe to newsletter" ]
group : List (Attribute id val) -> List (Field id) -> Field id

Groups a list of fields.

nameFields : Field id
nameFields =
    group
        [ name "person-name" ]
        [ text [ name "firstName", label "First Name" ]
        , text [ name "lastName", label "Last Name" ]
        ]
List (Attribute id val)
-> Field id
-> List (Field id -> Field id)
-> Field id

Builds a repeatable group of fields from a template field.

A list of field update functions can be passed as the second argument to apply to the template, creating one field per function.

The markup includes buttons for adding and removing fields.

Relevant attributes are repeatableMin, repeatableMax, and copies.

import FormToolkit.Parse as Parse
import FormToolkit.Value as Value


emailsFields : Field id
emailsFields =
    repeatable
        [ repeatableMin 1
        , repeatableMax 5
        , copies
            { addFieldsButton = "Add name"
            , removeFieldsButton = "Remove"
            }
        ]
        (text [ placeholder "Musician name" ])
        [ updateAttribute (stringValue "Brian Eno")
        , updateAttribute (stringValue "Faust")
        , updateAttribute (stringValue "Neu!")
        ]




emailsFields
    |> Parse.parse (Parse.list Parse.string)
--> Ok [ "Brian Eno", "Faust", "Neu!" ]

Attributes

type Attribute id val

Represents an attribute that can be applied to a field.

name : String -> Attribute id val

Sets the name of a field.

    import FormToolkit.Parse as Parse
    import Json.Encode


    text
        [ label "First name"
        , name "first-name"
        , value (Value.string "Chavela")
        ]
        |> Parse.parse Parse.json
            |> Result.map (Json.Encode.encode 0)
        --> Ok "{\"first-name\":\"Chavela\"}"
identifier : id -> Attribute id val

Sets the identifier to be referenced when decoding a specific field, extracting a segment of the form, updating the field's attributes, or customizing the rendering of a specific field or field error.

Any type can be used as an identifier, but using a custom type is encouraged for added type safety.

import FormToolkit.Parse as Parse
import FormToolkit.Value as Value


type Fields
    = FirstName
    | LastName


fields : Field Fields
fields =
    group []
        [ text
            [ label "First name"
            , identifier FirstName
            , value (Value.string "Juan")
            ]
        , text
            [ label "Last name"
            , identifier LastName
            , value (Value.string "Perez")
            ]
        ]


fields
    |> Parse.parse
        (Parse.field FirstName Parse.string)
    --> Ok "Juan"
value : Value -> Attribute id val

Sets the value of a field.

import FormToolkit.Parse as Parse
import FormToolkit.Value as Value


text [ label "Name", value (Value.string "Chavela") ]
    |> Parse.parse Parse.string
    --> Ok "Chavela"
stringValue : String -> Attribute id val

Sets string value of a field.

required : Bool -> Attribute id val

Marks a field as required, parsing and validation will fail and the missing field error will be displayed.

import FormToolkit.Parse as Parse
import FormToolkit.Value as Value


text [ label "First name" ]
    |> Parse.parse (Parse.maybe Parse.string)
    --> Ok Nothing
label : String -> Attribute id val

Sets the text to be rendered as the label for a field, or for the legend for a group or repeatable fields group.

placeholder : String -> Attribute id val

Sets the placeholder text of a field.

hint : String -> Attribute id val

Sets a hint or help text for a field.

selectionStart : Int -> Attribute id val

Sets the cursor start position for a field.

selectionEnd : Int -> Attribute id val

Sets the cursor end position for a field.

options : List ( String, Value ) -> Attribute id val

Sets the options for a select, radio button, or datalist for a text field or strictAutocomplete to provide autocomplete suggestions.

select
    [ label "Agreed?"
    , options [ ( "Yes", Value.bool True ), ( "No", Value.bool False ) ]
    ]
stringOptions : List String -> Attribute id val

Sets string options for a select, radio button, or datalist for a text field or strictAutocomplete to provide autocomplete suggestions.

flavorField : Field id ( Bool, Bool )
flavorField =
    text
        [ label "Favorite flavor"
        , stringOptions
            [ "Chocolate"
            , "Pistachio"
            , "Caramel salt"
            ]
        ]
min : Value -> Attribute id val

Sets the minimum value for a field input if its value is scalar: int, float, date, month, datetime, or time.

max : Value -> Attribute id val

Sets the maximum value for a field input if its value is scalar: int, float, date, month, datetime, or time.

step : Value -> Attribute id val

Sets the step value for numeric field inputs (int and float). The step value specifies the increment when using the input's up/down arrows.

autogrow : Bool -> Attribute id val

Makes a textarea autogrow.

class : String -> Attribute id val

Apply a CSS class

classList : List ( String, Bool ) -> Attribute id val

Apply a conditional list of CSS classes

disabled : Bool -> Attribute id val

Sets whether a field is disabled. When set to True, the field input will have the disabled HTML attribute.

hidden : Bool -> Attribute id val

Sets whether a field is hidden. When set to True, the field will not be rendered.

noattr : Attribute id val

An attribute that does nothing.

pattern : String -> Attribute id val

Sets a pattern for text input. Only characters matching the pattern will be allowed. The input will be formatted automatically, and on blur validated. Parsing will fail if input doesn't match the pattern.

  • {d} - matches digits
  • {D} - matches non-digits
  • {w} - matches word characters (alphanumeric + underscore)
  • {W} - matches non-word characters
  • Any other character is treated as a literal
text
    [ label "Phone Number"
    , pattern "({d}{d}{d}) {d}{d}{d}-{d}{d}{d}{d}"
    ]

Groups

{ addFieldsButton : String
, removeFieldsButton : String
}
-> Attribute id val

Sets the text for the add and remove buttons in a repeatable field.

repeatableMin : Int -> Attribute id val

Sets the minimum number of instances for a repeating field for a repeatable.

repeatableMax : Int -> Attribute id val

Sets the maximum number of instances for a repeating field for a repeatable.

Update attributes

updateAttribute : Attribute id val -> Field id -> Field id

Updates a field attribute.

import FormToolkit.Parse as Parse
import FormToolkit.Value as Value


text
    [ value (Value.string "Value") ]
    |> updateAttribute (value (Value.string "Updated"))
    |> Parse.parse Parse.string
    --> Ok "Updated"
updateAttributes : List (Attribute id val) -> Field id -> Field id

Updates several field attributes.

import FormToolkit.Parse as Parse
import FormToolkit.Value as Value


text
    [ identifier "Field"
    , stringOptions [ "Vanilla", "Lemon", "Yogurt" ]
    ]
    |> updateAttributes
        [ value (Value.string "Chocolate")
        , stringOptions
            [ "Chocolate"
            , "Pistachio"
            , "Caramel salt"
            ]
        ]
    |> Parse.parse Parse.string
    --> Ok "Chocolate"
updateWithId : id -> Attribute id val -> Field id -> Field id

Traverses the field tree updating a descendant field matching the identifier, if the descendant is not found it will return Nothing.

import FormToolkit.Parse as Parse
import FormToolkit.Value as Value


group []
    [ text
        [ identifier "Field"
        , value (Value.string "Value")
        ]
    ]
    |> updateWithId "Field" (value (Value.string "Updated"))
    |> Parse.parse (Parse.field "Field" Parse.string)
    --> Ok "Updated"
updateValuesFromJson : Value -> Field id -> Result (Error id) (Field id)

Sets field values from a JSON object. The JSON structure should match the form's nested field structure with name, groups with no name will not be structurally considered.

import Json.Decode as Decode
import Json.Encode as Encode
import FormToolkit.Parse as Parse




json : Encode.Value
json =
    """{"user":{"name":"Alice","email":"[email protected]"}}"""
        |> Decode.decodeString Decode.value
        |> Result.withDefault Encode.null




fields : Field String
fields =
    group []
        [ group [ name "user" ]
            [ text [ name "name" ]
            , text [ name "email", identifier "email-field" ]
            ]
        ]


updateValuesFromJson json fields
    |> Result.andThen (Parse.parse (Parse.field "email-field" Parse.string))
--> Ok "[email protected]"

Mapping and composition

map : (a -> b) -> Field a -> Field b

Transforms identifiers or errors in a field, useful for combining fields with identifiers of different types.

type PersonFields
    = PersonName
    | PersonAge


type TeamFields
    = TeamName
    | TeamMembers
    | MemberFields PersonFields


personsFields : Field PersonFields
personsFields =
    group []
        [ text
            [ label "Member Name"
            , identifier PersonName
            ]
        , int
            [ label "Member Age"
            , identifier PersonAge
            ]
        ]


teamFields : Field TeamFields
teamFields =
    group []
        [ text
            [ label "Team Name"
            , identifier TeamName
            ]
        , repeatable
            [ identifier TeamMembers ]
            -- ↓↓↓↓
            (map MemberFields personFields)
            -- ↑↑↑↑
            []
        ]

Validation

validate : Field id -> Field id

Check all contained inputs and display errors for failed validations.

touch : Field id -> Field id

Mark all inputs as touched displaying errors for invalid fields.