A Form is a record which contains functions that you can use in your
Elm Program to initialise, view, update, subscribe, and submit your form.
Test and debug a Control by turning it into a Program that you can run as a standalone Elm application.
import Control exposing (sandbox, int, State, Delta)
sandbox
{ control = int
, outputToString = Debug.toString
}
--: Program () (State String) (Delta String)This is useful when you're rapidly iterating on designing a form, and you don't yet want the hassle of plumbing it into
your main Elm application's Modeland Msg types.
Once you're happy with the form, you can then ask the Elm repl (or your editor's Elm plugin) to tell you the type
signature of the Program, which will give you the form's State and Delta types. You can then plug these into your
main Modeland Msg types wherever appropriate.
Convert a Control into a simple Form, rendered as a list of controls
wrapped in an HTML <form> element, with a submit button at the bottom.
You will need to supply a couple of variants from your app's Msg type: one to
handle updates of the form's state, and one to handle the submission of the
form, as follows:
import Control exposing (Form, Delta, simpleForm, int)
type Msg
= FormUpdated (Delta String)
| FormSubmitted
simpleForm
{ control = int
, onUpdate = FormUpdated
, onSubmit = FormSubmitted
}
--: Form String String Int MsgNow you can integrate your Form into a larger Elm app:
Call mySimpleForm.blank or mySimpleForm.prefill in your init function.
Call mySimpleForm.update in the FormUpdated branch of your update function.
Call mySimpleForm.submit in the FormSubmitted branch of your update function.
Call mySimpleForm.view in your view function.
Call mySimpleForm.subscriptions in your subscriptions function.
Convert a Control into a Form, and render the form however you like.
You will need to supply a variant from your app's Msg type to handle updates of the form's state.
You will also need to supply a view function that takes the List (Html msg) produced by the
supplied Control and returns an Html msg, as follows:
import Control exposing (Form, Delta, form, int)
import Html
import Html.Attributes
import Html.Events
type Msg
= FormUpdated (Delta String)
| FormSubmitted
form
{ control = int
, onUpdate = FormUpdated
, view =
\controlView ->
Html.form
[ Html.Events.onSubmit FormSubmitted ]
(controlView ++ [ Html.button [ Html.Attributes.type_ "submit" ] [ Html.text "Submit" ] ])
}
--: Form String String Int MsgNow you can integrate your Form into a larger Elm app:
Call myForm.blank or myForm.prefill in your init function.
Call myForm.update in the FormUpdated branch of your update function.
Call myForm.submit in the FormSubmitted branch of your update function.
Call myForm.view in your view function.
Call myForm.subscriptions in your subscriptions function.
A control that produces a Bool. Renders as an HTML checkbox. Default output is False.
import Control exposing (Control, bool)
bool
--: Control context Bool Bool BoolA control that produces an Int. Renders as an HTML number input.
import Control exposing (Control, int)
int
--: Control context String String IntA control that produces a Float. Renders as an HTML number input.
import Control exposing (Control, float)
float
--: Control context String String FloatA control that produces a String. Renders as an HTML text input.
import Control exposing (Control, string)
string
--: Control context String String StringA control that produces a Char. Renders as an HTML text input.
import Control exposing (Control, char)
char
--: Control context String String CharA control that produces a custom type where none of the variants have any arguments. Renders as an HTML radio input. Defaults to the first variant.
import Control exposing (Control, enum)
type Colour
= Red
| Green
| Blue
enum
( "Red", Red )
( "Green", Green )
[ ( "Blue", Blue ) ]
--: Control context Colour Colour ColourA type used to contain the states or deltas of a tuple control.
A combinator that produces a tuple of two controls of given types.
import Control exposing (Control, Tuple, tuple, string, bool)
tuple string bool
--: Control context (Tuple String Bool) (Tuple String Bool) ( String, Bool )A type used to contain the states or deltas of a triple control.
A combinator that produces a triple of three controls of given types.
import Control exposing (Control, Triple, triple, string, bool, float)
triple string bool float
--: Control context (Triple String Bool String) (Triple String Bool String) ( String, Bool, Float )A type used to contain the state or delta types for a maybe control.
A combinator that produces a Maybe of a control of a given type.
import Control exposing (Control, Maybe_, int, maybe)
maybe int
--: Control context (Maybe_ String) (Maybe_ String) (Maybe Int)A type used to contain the state or delta types for a result control.
A combinator that produces a Result with a given error and
success type.
import Control exposing (Control, Result_, result, string, bool)
result string bool
--: Control context (Result_ String Bool) (Result_ String Bool) (Result String Bool)A type used to contain the state or delta of a list control.
A combinator that produces a List of controls of a given type.
import Control exposing (Control, List_, list, bool)
list bool
--: Control context (List_ Bool) (List_ Bool) (List Bool)A type used to contain the state or delta of a dict control.
A combinator that produces a Dict from controls of given key and value
types. The key type must be comparable.
import Control exposing (Control, Dict_, dict, string, bool)
import Dict
dict string bool
--: Control context (Dict_ String Bool) (Dict_ String Bool) (Dict.Dict String Bool)A type used to contain the state or delta of a set control.
A combinator that produces a Set from controls of a given member
types. The member type must be comparable.
import Control exposing (Control, Set_, set, string)
import Set
set string
--: Control context (Set_ String) (Set_ String) (Set.Set String)A type used to contain the state or delta of an array control.
A combinator that produces an Array from a control of a given type.
import Control exposing (Control, Array_, array, int)
import Array
array int
--: Control context (Array_ String) (Array_ String) (Array.Array Int)A type used to contain the state or delta type for a control whose output has been converted using map.
A combinator that converts a Control whose output is of type a to a Control whose output is of type b.
This is particularly useful for implementing "wrapper" types, such as Ids.
Note: with most common map functions, such as List.map, we only need to supply a function from a -> b. In this case,
however, we need to supply two functions that will allow us to both convert the type from a -> b and
revert it back from b -> a.
import Control exposing (Control, Mapping, int, map)
type Id
= Id Int
int
|> map
{ convert = \i -> Id i
, revert = \(Id i) -> i
}
--: Control context (Mapping String) (Mapping String) IdA type used to contain the state or delta of a record control.
A type used to contain the state or delta of a field within a record control.
A type used to mark the end of the type annotation for a custom type control.
A data structure used to build records
A combinator that produces a record type.
import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, string, int)
type alias MyRecord =
{ foo : String
, bar : Int
}
record
(\foo bar ->
{ foo = foo
, bar = bar
}
)
|> field .foo string
|> field .bar int
|> endRecord
--: Control context (Record (Field String (Field String EndRecord))) (Record (Field String (Field String EndRecord))) MyRecordAdd a field to a record combinator.
import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, string)
type alias Hello =
{ hello : String }
record Hello
|> field .hello string
|> endRecord
--: Control context (Record (Field String EndRecord)) (Record (Field String EndRecord)) HelloFinalise the construction of a record combinator.
import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, string)
type alias Hello =
{ hello : String }
record Hello
|> field .hello string
|> endRecord
--: Control context (Record (Field String EndRecord)) (Record (Field String EndRecord)) HelloConfiguration for the layout function
Data used by the layout function to render a subcontrol of a combinator.
Define the HTML output for a combinator.
This is useful if you want to alter the way that the combinator's subcontrols are laid out on the screen.
For example, you could use it to present a record control as a wizard, rather than a list. The wizard example below is very naive, but hopefully gives you the idea.
import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, layout, string, int)
import Html
import Html.Attributes
import Html.Events
type alias MyRecord =
{ foo : String
, bar : Int
}
record
(\foo bar ->
{ foo = foo
, bar = bar
}
)
|> field .foo string
|> field .bar int
|> endRecord
|> layout wizard
--: Control context (Record (Field String (Field String EndRecord))) (Record (Field String (Field String EndRecord))) MyRecord
wizard config subcontrols =
let
currentPage =
config.selected
totalPages =
List.length subcontrols
currentPageView =
subcontrols
|> List.filter (\{ index } -> index == currentPage)
|> List.map .html
|> List.concat
nextClicked =
config.selectMsg (currentPage + 1)
backClicked =
config.selectMsg (currentPage - 1)
navigationButton txt msg =
Html.button
[ Html.Attributes.type_ "button"
, Html.Events.onClick msg
]
[ Html.text txt ]
in
[ Html.div
[ Html.Attributes.id config.id
, Html.Attributes.class config.class
]
([ Html.h1 [] [ Html.text "Wizard!" ]
, navigationButton "Back" backClicked
, Html.text
(String.join " "
[ "page"
, String.fromInt currentPage
, "of"
, String.fromInt totalPages
]
)
, navigationButton "Next" nextClicked
]
++ currentPageView
)
]A type used to contain the state or delta of a custom type control.
A type used to contain the state or delta of a variant for a custom type control.
A type that can contain the state or delta of an argument for a variant within a custom type control.
A type used to mark the end of the type annotation for a variant within a custom type control.
A type used to mark the end of the type annotation for a custom type control.
A data structure used to build custom types
A combinator that produces a custom type.
import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, variant0, variant1, variant2, endCustomType, string, int, float)
type MyCustomType
= NoArgs
| OneArg String
| TwoArgs Int Float
customType
(\noArgs oneArg twoArgs variant ->
case variant of
NoArgs ->
noArgs
OneArg arg1 ->
oneArg arg1
TwoArgs arg1 arg2 ->
twoArgs arg1 arg2
)
|> variant0 "NoArgs" NoArgs
|> variant1 "OneArg" OneArg string
|> variant2 "TwoArgs" TwoArgs int float
|> endCustomType
--: Control context (Control.CustomType (Control.Variant Control.EndVariant (Control.Variant (Control.Arg String Control.EndVariant) (Control.Variant (Control.Arg String (Control.Arg String Control.EndVariant)) Control.EndCustomType)))) (Control.CustomType (Control.Variant Control.EndVariant (Control.Variant (Control.Arg String Control.EndVariant) (Control.Variant (Control.Arg String (Control.Arg String Control.EndVariant)) Control.EndCustomType)))) MyCustomTypeAdd a variant with no arguments to a custom type.
import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, endCustomType, variant0)
type Unit
= Unit
customType
(\unit variant ->
case variant of
Unit ->
unit
)
|> variant0 "Unit" Unit
|> endCustomType
--: Control context (CustomType (Variant EndVariant EndCustomType)) (CustomType (Variant EndVariant EndCustomType)) UnitAdd a variant with one argument to a custom type.
import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, endCustomType, variant1, int, string)
type alias MyResult =
Result String Int
customType
(\ok err variant ->
case variant of
Ok value ->
ok value
Err error ->
err error
)
|> variant1 "Ok" Ok int
|> variant1 "Err" Err string
|> endCustomType
--: Control context (CustomType (Variant (Arg String EndVariant) (Variant (Arg String EndVariant) EndCustomType))) (CustomType (Variant (Arg String EndVariant) (Variant (Arg String EndVariant) EndCustomType))) MyResultAdd a variant with two arguments to a custom type.
import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, variant2, endCustomType, float)
type Point
= Point Float Float
customType
(\point variant ->
case variant of
Point x y ->
point x y
)
|> variant2 "Point" Point float float
|> endCustomType
--: Control context (CustomType (Variant (Arg String (Arg String EndVariant)) EndCustomType)) (CustomType (Variant (Arg String (Arg String EndVariant)) EndCustomType)) PointAdd a variant with three arguments to a custom type.
import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, variant3, endCustomType, float)
type Point3D
= Point3D Float Float Float
customType
(\point3D variant ->
case variant of
Point3D x y z ->
point3D x y z
)
|> variant3 "Point3D" Point3D float float float
|> endCustomType
--: Control context (CustomType (Variant (Arg String (Arg String (Arg String EndVariant))) EndCustomType)) (CustomType (Variant (Arg String (Arg String (Arg String EndVariant))) EndCustomType)) Point3DAdd a variant with four arguments to a custom type.
Add a variant with five arguments to a custom type.
Finalise the construction of a customType combinator.
type alias Foo
= Bar
| Baz
helloControl =
customType
(\bar baz variant ->
case variant of
Bar -> bar
Baz -> baz
)
|> variant0 "Bar" Bar
|> variant0 "Baz" Baz
|> endCustomTypeDefinition for a custom control. You need to supply the following fields:
blank: an initial "empty" value for the state of the control, plus an initial (optional) Cmd to run. For a control whose state is of type String, the state value might be "", for a number it might be 0.prefill: a function that uses a value of the control's output type to initialise the control's state and (optionally) run an initial Cmd. For a control that outputs an Int, but whose state is a String, you might use String.fromInt to convert the output value into a state value.update: a function that updates the state of the control based on its existing state and a delta type that it receives. This is exactly analogous to an Elm program's update function, where state = Model and delta = Msg.view: a function that outputs Html delta based on the state of the control. This is similar to an Elm program's view function, except instead of just taking the state as an argument, it takes a record containing the state plus the id, name, class, and label for the input.subscriptions: a function that outputs Sub delta based on the state of the control. This is exactly analogous to an Elm program's subscriptions function.parse: a function that attempts to parse the control's state into its output type, producing a result. If parsing fails, it can provide a list of errors.label: a default label for the control.Define a new type of Control, with any arbitrary state and delta types,
and producing any arbitrary output type.
Here's how we could define a control like the famous counter example from the Elm Guide
import Control exposing (Control, define)
import Html
import Html.Attributes
import Html.Events
type CounterDelta
= Increment
| Decrement
define
{ blank = ( 0, Cmd.none )
, prefill = \n -> ( n, Cmd.none )
, update =
\delta state ->
case delta of
Increment ->
( state + 1, Cmd.none )
Decrement ->
( state - 1, Cmd.none )
, view =
\{ state, name, id, label, class } ->
[ Html.div [ Html.Attributes.class class ]
[ Html.label
[ Html.Attributes.for id ]
[ Html.text label ]
, Html.div
[ Html.Attributes.id id
, Html.Attributes.name name
]
[ Html.button
[ Html.Attributes.type_ "button"
, Html.Events.onClick Increment
]
[ Html.text "+" ]
, Html.div [] [ Html.text <| String.fromInt state ]
, Html.button
[ Html.Attributes.type_ "button"
, Html.Events.onClick Decrement
]
[ Html.text "-" ]
]
]
]
, subscriptions = \state -> Sub.none
, parse = Ok
, label = "Counter"
}
--: Control context Int CounterDelta IntDisplay an error on a Control if the supplied predicate function returns
True.
This causes the Control to fail validation.
import Control exposing (Control, failIf, int)
int
|> failIf
(\x -> x < 1)
"This must be greater than zero!"
--: Control context String String IntDisplay a notification on a Control if the supplied predicate function
returns True.
This just shows the user a message - it doesn't cause the Control to fail
validation.
import Control exposing (Control, int, noteIf)
int
|> noteIf
(\x -> x < 1)
"Maybe this should be greater than zero?"
--: Control context String String IntYou may want display errors on one or more controls based on the validation of another control higher up the tree.
Take the example of a password creation form. You want to validate that the "Enter password" and "Confirm password" fields both contain the same string, and show a helpful error message on the "Confirm password" field if they don't. You can achieve this as follows:
import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, string, alertIf, respond)
record
(\password confirmation ->
{ password = password
, confirmation = confirmation
}
)
|> field .password string
|> field .confirmation
(string
|> respond
{ alert = "passwords-do-not-match"
, fail = True
, message = "Passwords must match"
, class = "control-feedback-fail"
}
)
|> endRecord
|> alertIf
(\{ password, confirmation } ->
password /= confirmation
)
"passwords-do-not-match"
--: Control context (Record (Field String (Field String EndRecord))) (Record (Field String (Field String EndRecord))) { password : String, confirmation : String }Emit an alert from a Control if the supplied predicate function returns
True.
This is most useful with control combinators such as tuple or record,
because it enables you to perform validations that involve multiple fields
within the tuple or record.
import Control exposing (Control, Tuple, alertIf, tuple, int, string)
tuple int string
|> alertIf (\( x, y ) -> x > 0) "int-greater-than-zero"
--: Control context (Tuple String String) (Tuple String String) ( Int, String )This is meant to be used in combination with respond, which listens for the
alert and displays an error or notification message on the appropriate
control(s).
Display an error or notification message on a Control in response to an
alert emitted by another Control. If desired, cause validation to fail.
import Control exposing (Control, respond, int)
int
|> respond
{ alert = "int-greater-than-zero"
, fail = True
, message = "Must be less than zero"
, class = "control-feedback-fail"
}
--: Control context String String IntThis is meant to be used in combination with alertIf, which emits the alert.
Conditionally display an error on one or more items in a list control,
based on the output of the list control.
The following example will display errors on the first two items in a list of strings, if and only if those first two items are "hello" and "world":
import Control exposing (Control, List_, alertAtIndexes, list, respond, string)
myString =
string
|> respond
{ alert = "no-hello-world"
, fail = True
, message = "List can't start with \"hello\" and \"world\"."
, class = "control-feedback-fail"
}
list myString
|> alertAtIndexes
(\list_ ->
case list_ of
"hello" :: "world" :: _ ->
[ 0, 1 ]
_ ->
[]
)
"no-hello-world"
--: Control context (List_ String) (List_ String) (List String)Set a default output value for a Control.
import Control exposing (Control, Tuple, default, int, string, tuple)
tuple int string
|> default ( 1, "hello" )
--: Control context (Tuple String String) (Tuple String String) ( Int, String )Debounce a Control for a number of milliseconds before it will display
any parsing or validation errors. This can be used to allow the user to finish
typing into a text input before we show them feedback.
import Control exposing (Control, string, debounce)
string
|> debounce 2000
--: Control context String String StringSet an id for a Control.
import Control exposing (Control, string, id)
string
|> id "my-string"
--: Control context String String StringSet a name for a Control.
import Control exposing (Control, string, name)
string
|> name "My string"
--: Control context String String StringSet a label for a Control.
import Control exposing (Control, string, label)
string
|> label "Enter your name"
--: Control context String String StringAdd an HTML class attribute for a Control. See docs for Html.Attributes.class in elm-html.
import Control exposing (Control, string, class)
string
|> class "important"
|> class "no-really"
--: Control context String String StringAdd a list of HTML class attributes to a Control. See docs for Html.Attributes.classList in the elm/html
package.
import Control exposing (Control, string, classList)
string
|> classList
[ ( "important", True )
, ( "no-really", True )
, ( "ignore-me", False )
]
--: Control context String String StringTransform the HTML output of a Control's view function.
import Control exposing (Control, int, wrapView)
import Html
int
|> wrapView (\view -> [ Html.div [] view ])
--: Control context String String IntFor some advanced use cases, you may want to create a form that knows about more
than its own internal state. It may also need to know about some state that
lives elsewhere in your app's Model.
This is where the ...WithContext family of types and functions come into play.
They allow you to inject some external state into your form controls by
passing a context value as an additional argument to the form's update,
view, subscriptions and submit functions.
You can also pass context into any validations you apply to your Controls
using failIfWithContext, noteIfWithContext etc.
A FormWithContext is a record which contains functions that you can use in your
Elm Program to initialise, view, update, subscribe, and submit your form.
Unlike the standard Form, several of the functions provided by FormWithContext
take an extra context parameter, which allows you to feed external data (e.g. from
your app's Model) into your form and allow the form to respond accordingly.
For example, imagine your app's Model contains a list of users, and you build
a form to create a new user. The new user should only be valid if their name
hasn't already been chosen by another user.
We can ensure this by passing the set of existing user names into the form's
update and submit functions as context:
import Control exposing (Control, FormWithContext, State, Delta, string, failIfWithContext, simpleFormWithContext)
import Set
type alias User =
String
type alias Model =
{ formState : State String
, existingUsers : Set.Set String
}
type alias Context =
Set.Set String
type Msg
= FormUpdated (Delta String)
| FormSubmitted
userControl =
string
|> failIfWithContext
(\existingUsers newUser ->
Set.member newUser existingUsers
)
"User already exists"
userControl
--: Control Context String String String
form =
simpleFormWithContext
{ control = userControl
, onUpdate = FormUpdated
, onSubmit = FormSubmitted
}
form
--: FormWithContext Context String String String Msg
update msg model =
let
context =
model.existingUsers
in
case msg of
FormUpdated delta ->
let
(formState, cmd) =
form.update context delta model.formState
in
( { model | formState = formState }, cmd )
FormSubmitted ->
let
(newFormState, formResult) =
form.submit context model.formState
in
case formResult of
Ok user ->
let
( blankFormState, cmd ) =
form.blank
in
( { model
| existingUsers = Set.insert user model.existingUsers
, formState = blankFormState
}
, cmd
)
Err err ->
( { model | formState = newFormState }
, Cmd.none
)Convert a Control into a simple FormWithContext, rendered as a list of
controls wrapped in an HTML <form> element, with a submit button at the bottom.
To use this function, follow the instructions in the documentation for
simpleForm. The only difference is that when you call mySimpleForm.update,
mySimpleForm.submit, mySimpleForm.view and mySimpleForm.subscriptions, you
will need to supply a context value as the first argument.
Convert a Control into a FormWithContext, and render the form however
you like.
To use this function, follow the instructions in the documentation for
form. The only difference is that when you call myForm.update,
myForm.submit, myForm.view and myForm.subscriptions, you will need to
supply a context value as the first argument.
Definition for a custom control that is aware of its context.
This is exactly like the standard Definition type, except that the update,
view, subscriptions and parse functions all take an additional context
as their first argument.
Define a new type of Control, with any arbitrary state, delta and
output types, and that is aware of whatever context you choose to supply.
Here's a silly version of the counter example
where we use a context to supply maximum and minimum bounds that the counter
should stay within. We then implement various measures in the update, view, parse
and subscriptions functions to try to prevent the count from going outside those
bounds.
import Control exposing (Control, defineWithContext)
import Html
import Html.Attributes
import Html.Events
import Time
type CounterDelta
= Increment
| Decrement
type alias Context =
{ maxCount : Int, minCount : Int }
defineWithContext
{ blank = ( 0, Cmd.none )
, prefill = \x -> ( x, Cmd.none )
, update =
\context delta state ->
-- When an increment/decrement message comes in, check whether
-- it would cause the count to go beyond the bounds set by the
-- context
(case delta of
Increment -> if state < context.maxCount then state + 1 else state
Decrement -> if state > context.minCount then state - 1 else state
, Cmd.none
)
, view =
\context { state, name, id, label, class } ->
-- Disable the increment/decrement buttons if they would
-- cause the count to go beyond the bounds set by the context
[ Html.div [ Html.Attributes.class class ]
[ Html.label
[ Html.Attributes.for id ]
[ Html.text label ]
, Html.div
[ Html.Attributes.id id
, Html.Attributes.name name
]
[ Html.button
[ Html.Attributes.type_ "button"
, Html.Events.onClick Increment
, Html.Attributes.disabled (state >= context.maxCount)
]
[ Html.text "+" ]
, Html.div [] [ Html.text <| String.fromInt state ]
, Html.button
[ Html.Attributes.type_ "button"
, Html.Events.onClick Decrement
, Html.Attributes.disabled (state <= context.minCount)
]
[ Html.text "-" ]
]
]
]
, subscriptions =
\context state ->
-- Every 1 second, increment or decrement the count until
-- it's back within the bounds set by the context
if state > context.maxCount then
Time.every 1000 (\_ -> Decrement)
else if state < context.minCount then
Time.every 1000 (\_ -> Increment)
else
Sub.none
, parse =
\context state ->
-- Fail validation if the count is outside the bounds set
-- by the context
if state > context.maxCount || state < context.minCount then
Err [ "count is out of bounds!" ]
else
Ok state
, label = "Counter"
}
--: Control Context Int CounterDelta IntDisplay an error on a Control if the supplied predicate function returns
True.
This causes the Control to fail validation.
import Control exposing (Control, failIfWithContext, int)
type alias Context =
{ minimumValue : Int }
int
|> failIfWithContext
(\context x -> x < context.minimumValue)
"This is less than the minimum value!"
--: Control Context String String IntDisplay a notification on a Control if the supplied predicate function
returns True.
This does not cause the Control to fail validation.
import Control exposing (Control, int, noteIfWithContext)
type alias Context =
{ recommendedMinimumValue : Int }
int
|> noteIfWithContext
(\context x -> x < context.recommendedMinimumValue)
"This value is below the recommended minimum - are you sure?"
--: Control Context String String IntEmit an alert from a Control if the supplied predicate function returns
True; another control can then react to this alert by using respond.
alertIfWithContext works exactly like alertIf, except that its predicate
function takes a context as its first argument.
Conditionally display an error on one or more items in a list control,
based on the output of the list control and the supplied context.
These are types that you will see in your form's State and Delta type signatures.
State is the form's equivalent of an Elm program's Model type. It is
used to manage the form's internal state.
A Delta is the form's equivalent of an Elm program's Msg type. It is
used internally within a form to update its state.
A user of this package shouldn't need to know about any of these types - they are only exposed to make it possible to write type signatures.
A slightly more flexible version of Control, which is only used
internally. As a user of the package, you shouldn't need to worry about this
type.
Some internal stuff needed to build up record and custom type controls
Some internal stuff needed to handle validations
Some internal stuff needed to build up a record control
An internal type needed to represent the validation state of a control
Some internal stuff needed to view controls
An internal type needed to track the position of a control within a tree of controls
Some internal stuff to handle validation
Under the hood, this library encodes complex Elm types (such as records and
custom types) as nested tuples, which are used in both the State and Delta
types for each Control. The End type is used to mark the end of a nested
tuple.
For example, the State for this Elm type:
type alias User =
{ name : String, age : Int }Could be encoded as:
( String, ( Int, End ) )
A
Controlis the basic unit from which forms are composed. This library provides various example controls and combinators that you can use to build a form for any arbitrarily complex Elm type.