Build composable forms comprised of fields.
Create a form that contains a single text field.
It requires some configuration:
parser specifies how to validate the field. It needs a function that processes the value of
the field and produces a Result of either:String describing an erroroutputvalue describes how to obtain the field value from the form valuesupdate describes how the current form values should be updated with a new field valueattributes let you define the specific attributes of the field (label and placeholder
in this case, see TextField.Attributes)It might seem like a lot of configuration, but don't be scared! In practice, it isn't!
For instance, you could use this function to build a nameField that only succeeds when the
inputted name has at least 2 characters, like this:
nameField : Form { r | name : String } String
nameField =
Form.textField
{ parser =
\name ->
if String.length name < 2 then
Err "the name must have at least 2 characters"
else
Ok name
, value = .name
, update =
\newValue values ->
{ values | name = newValue }
, attributes =
{ label = "Name"
, placeholder = "Type your name..."
}
}As you can see:
parser is just a simple validation functionvalue using record accessorsupdate function updates the values of the form with the newValueattributes are most of the time a simple recordCreate a form that contains a single email field.
It has the same configuration options as textField.
Create a form that contains a single password field.
It has the same configuration options as textField.
Create a form that contains a single textarea field.
It has the same configuration options as textField.
Create a form that contains a single number field.
It has a very similar configuration to textField, the only difference is:
NumberField.Attributes
instead of TextField.Attributes.Create a form that contains a single range field.
It has a very similar configuration to textField, the only difference is:
RangeField.Attributes
instead of TextField.Attributes.Create a form that contains a single checkbox field.
It has a very similar configuration to textField, the only differences are:
Bool instead of String.CheckboxField.Attributes
instead of TextField.Attributes.Create a form that contains a single fieldset of radio fields.
It has a very similar configuration to textField, the only difference is:
RadioField.Attributes
instead of TextField.Attributes.Create a form that contains a single select field.
It has a very similar configuration to textField, the only difference is:
SelectField.Attributes
instead of TextField.Attributes.All the functions in the previous section produce a Form with a single
field. You might then be wondering: "How do I create a Form with multiple fields?!"
Well, as the name of this package says: Form is composable! This section explains how you
can combine different forms into bigger and more complex ones.
Create an empty form that always succeeds when filled, returning the given output.
It might seem pointless on its own, but it becomes useful when used in combination with other
functions. The docs for append have some great examples.
Append a form to another one while capturing the output of the first one.
For instance, we could build a signup form:
signupEmailField : Form { r | email : String } EmailAddress
signupEmailField =
Form.emailField
{ -- ...
}
signupPasswordField : Form { r | password : String } Password
signupPasswordField =
Form.passwordField
{ -- ...
}
signupForm :
Form
{ email : String
, password : String
}
( EmailAddress, Password )
signupForm =
Form.succeed Tuple.pair
|> Form.append signupEmailField
|> Form.append signupPasswordFieldIn this pipeline, append is being used to feed the Tuple.pair function and combine two forms
into a bigger form that outputs ( EmailAddress, Password ) when submitted.
Note: You can use succeed smartly to skip some values.
This is useful when you want to append some fields in your form to perform validation, but
you do not care about the output they produce. An example of this is a "repeat password" field:
passwordForm :
Form
{ password : String
, repeatPassword : String
}
Password
passwordForm =
Form.succeed (\password repeatedPassword -> password)
|> Form.append passwordField
|> Form.append repeatPasswordFieldMake a form optional. An optional form succeeds when:
NothingJust the outputLet's say we want to optionally ask for a website name and address:
websiteForm =
Form.optional
(Form.succeed Website
|> Form.append websiteNameField
|> Form.append websiteAddressField
)This websiteForm will only be valid if both fields are blank, or both fields
are filled correctly.
Wraps a form in a group.
Using this function does not affect the behavior of the form in any way. However, groups of fields
might be rendered differently. For instance, Form.View renders groups of
fields horizontally.
Wraps a form in a section: an area with a title.
Like group, this function has no effect on form behavior. It just
indicates to the form view function that the fields are part of some user-defined
section.
Fill a form andThen fill another one.
This is useful to build dynamic forms. For instance, you could use the output of a selectField
to choose between different forms, like this:
type Msg
= CreatePost Post.Body
| CreateQuestion Question.Title Question.Body
type ContentType
= Post
| Question
type alias Values =
{ type_ : String
, title : String
, body : String
}
contentForm : Form Values Msg
contentForm =
Form.selectField
{ parser =
\value ->
case value of
"post" ->
Ok Post
"question" ->
Ok Question
_ ->
Err "invalid content type"
, value = .type_
, update = \newValue values -> { values | type_ = newValue }
, attributes =
{ label = "Which type of content do you want to create?"
, placeholder = "Choose a type of content"
, options = [ ( "post", "Post" ), ( "question", "Question" ) ]
}
}
|> Form.andThen
(\contentType ->
case contentType of
Post ->
let
bodyField =
Form.textareaField
{ -- ...
}
in
Form.succeed CreatePost
|> Form.append bodyField
Question ->
let
titleField =
Form.textField
{ -- ...
}
bodyField =
Form.textareaField
{ -- ...
}
in
Form.succeed CreateQuestion
|> Form.append titleField
|> Form.append bodyField
)Build a form that depends on its own values.
This is useful when you need some fields to perform validation based on the values of other fields. An example of this is a "repeat password" field:
repeatPasswordField :
Form
{ r
| password : String
, repeatPassword : String
}
()
repeatPasswordField =
Form.meta
(\values ->
Form.passwordField
{ parser =
\value ->
if value == values.password then
Ok ()
else
Err "the passwords do not match"
, value = .repeatPassword
, update =
\newValue values ->
{ values | repeatPassword = newValue }
, attributes =
{ label = "Repeat password"
, placeholder = "Type your password again..."
}
}
)Build a variable list of forms.
For instance, you can build a form that asks for a variable number of websites:
type alias WebsiteValues =
{ name : String
, address : String
}
websiteForm : Int -> Form WebsiteValues Website
websitesForm : Form { r | websites : List WebsiteValues } (List Website)
websitesForm =
Form.list
{ default =
{ name = ""
, address = "https://"
}
, value = .websites
, update = \value values -> { values | websites = value }
, attributes =
{ label = "Websites"
, add = Just "Add website"
, delete = Just ""
}
}
websiteFormTransform the output of a form.
This function can help you to keep forms decoupled from specific view messages:
Form.map SignUp signupFormTransform the values of a form.
This can be useful when you need to nest forms:
type alias SignupValues =
{ email : String
, password : String
, address : AddressValues
}
addressForm : Form AddressValues Address
signupForm : Form SignupValues Msg
signupForm =
Form.succeed SignUp
|> Form.append emailField
|> Form.append passwordField
|> Form.append
(Form.mapValues
{ value = .address
, update = \newAddress values -> { values | address = newAddress }
}
addressForm
)This section describes how to fill a Form with its values and obtain its
different fields and its output. This is mostly used to write custom view code.
If you just want to render a simple form as Html, check Form.View first as it
might suit your needs.
Represents a form field.
If you are writing custom view code you will probably need to pattern match this type,
using the result of fill.
Represents a type of text field
Fill a form with some values.
It returns:
output
A
Formcollects and validates user input using fields. When a form is filled withvalues, it produces someoutputif validation succeeds.For example, a
Form String EmailAddressis a form that is filled with aStringand produces anEmailAddresswhen valid. This form could very well be anemailField!A
Formis only the definition of your form logic! It only represents the shape of a form! It lives on its own, decoupled from its values, the rendering strategy and view state.If you like to learn by example, you can check out this excellent introduction to the package by Alex Korban.