mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-11-25 09:21:57 +03:00
Update docs.
This commit is contained in:
parent
fb8e23e5b6
commit
60607c3353
194
src/Form.elm
194
src/Form.elm
@ -13,7 +13,199 @@ module Form exposing
|
|||||||
-- subGroup
|
-- subGroup
|
||||||
)
|
)
|
||||||
|
|
||||||
{-|
|
{-| One of the core features of elm-pages is helping you manage form data end-to-end, including
|
||||||
|
|
||||||
|
- Presenting the HTML form with its fields
|
||||||
|
- Maintaining client-side form state
|
||||||
|
- Showing validation errors on the client-side
|
||||||
|
- Receiving a form submission on the server-side
|
||||||
|
- Using the exact same client-side validations on the server-side
|
||||||
|
- Letting you run server-only Validations with DataSource's (things like checking for a unique username)
|
||||||
|
|
||||||
|
Because elm-pages is a framework, it has its own internal Model and Msg's. That means you, the user,
|
||||||
|
can offload some of the responsibility to elm-pages and build an interactive form with real-time
|
||||||
|
client-side state and validation errors without wiring up your own Model and Msg's to manage that
|
||||||
|
state. You define the source of truth for your form (how to parse it into data or errors), and
|
||||||
|
elm-pages manages the state.
|
||||||
|
|
||||||
|
Let's look at a sign-up form example.
|
||||||
|
|
||||||
|
|
||||||
|
### Step 1 - Define the Form
|
||||||
|
|
||||||
|
What to look for:
|
||||||
|
|
||||||
|
**The field declarations**
|
||||||
|
|
||||||
|
Below the `Form.init` call you will find all of the form's fields declared with
|
||||||
|
|
||||||
|
|> Form.field ...
|
||||||
|
|
||||||
|
These are the form's field declarations.
|
||||||
|
|
||||||
|
These fields each have individual validations. For example, `|> Field.required ...` means we'll get a validation
|
||||||
|
error if that field is empty (similar for checking the minimum password length).
|
||||||
|
|
||||||
|
There will be a corresponding parameter in the function we pass in to `Form.init` for every
|
||||||
|
field declaration (in this example, `\email password passwordConfirmation -> ...`).
|
||||||
|
|
||||||
|
**The `combine` validation**
|
||||||
|
|
||||||
|
In addition to the validation errors that individual fields can have independently (like
|
||||||
|
required fields or minimum password length), we can also do _dependent validations_.
|
||||||
|
|
||||||
|
We use the [`Form.Validation`](Form-Validation) module to take each individual field and combine
|
||||||
|
them into a type and/or errors.
|
||||||
|
|
||||||
|
**The `view`**
|
||||||
|
|
||||||
|
Totally customizable. Uses [`Form.FieldView`](Form-FieldView) to render all of the fields declared.
|
||||||
|
|
||||||
|
import DataSource exposing (DataSource)
|
||||||
|
import ErrorPage exposing (ErrorPage)
|
||||||
|
import Form
|
||||||
|
import Form.Field as Field
|
||||||
|
import Form.FieldView as FieldView
|
||||||
|
import Form.Validation as Validation
|
||||||
|
import Html exposing (Html)
|
||||||
|
import Html.Attributes as Attr
|
||||||
|
import Route
|
||||||
|
import Server.Request as Request
|
||||||
|
import Server.Response exposing (Response)
|
||||||
|
|
||||||
|
type alias NewUser =
|
||||||
|
{ email : String
|
||||||
|
, password : String
|
||||||
|
}
|
||||||
|
|
||||||
|
signupForm : Form.HtmlForm String NewUser () Msg
|
||||||
|
signupForm =
|
||||||
|
Form.init
|
||||||
|
(\email password passwordConfirmation ->
|
||||||
|
{ combine =
|
||||||
|
Validation.succeed Login
|
||||||
|
|> Validation.andMap email
|
||||||
|
|> Validation.andMap
|
||||||
|
(Validation.map2
|
||||||
|
(\pass confirmation ->
|
||||||
|
if pass == confirmation then
|
||||||
|
Validation.succeed pass
|
||||||
|
|
||||||
|
else
|
||||||
|
passwordConfirmation
|
||||||
|
|> Validation.fail
|
||||||
|
"Must match password"
|
||||||
|
)
|
||||||
|
password
|
||||||
|
passwordConfirmation
|
||||||
|
|> Validation.andThen identity
|
||||||
|
)
|
||||||
|
, view =
|
||||||
|
\info ->
|
||||||
|
[ Html.label []
|
||||||
|
[ fieldView info "Email" email
|
||||||
|
, fieldView info "Password" password
|
||||||
|
, fieldView info "Confirm Password" passwordConfirmation
|
||||||
|
]
|
||||||
|
, Html.button []
|
||||||
|
[ if info.isTransitioning then
|
||||||
|
Html.text "Signing Up..."
|
||||||
|
|
||||||
|
else
|
||||||
|
Html.text "Sign Up"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> Form.field "email"
|
||||||
|
(Field.text
|
||||||
|
|> Field.required "Required"
|
||||||
|
)
|
||||||
|
|> Form.field "password"
|
||||||
|
passwordField
|
||||||
|
|> Form.field "passwordConfirmation"
|
||||||
|
passwordField
|
||||||
|
|
||||||
|
passwordField =
|
||||||
|
Field.text
|
||||||
|
|> Field.password
|
||||||
|
|> Field.required "Required"
|
||||||
|
|> Field.withClientValidation
|
||||||
|
(\password ->
|
||||||
|
( Just password
|
||||||
|
, if String.length password < 4 then
|
||||||
|
[ "Must be at least 4 characters" ]
|
||||||
|
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fieldView :
|
||||||
|
Form.Context String data
|
||||||
|
-> String
|
||||||
|
-> Validation.Field String parsed FieldView.Input
|
||||||
|
-> Html msg
|
||||||
|
fieldView formState label field =
|
||||||
|
Html.div []
|
||||||
|
[ Html.label []
|
||||||
|
[ Html.text (label ++ " ")
|
||||||
|
, field |> Form.FieldView.input []
|
||||||
|
]
|
||||||
|
, (if formState.submitAttempted then
|
||||||
|
formState.errors
|
||||||
|
|> Form.errorsForField field
|
||||||
|
|> List.map
|
||||||
|
(\error ->
|
||||||
|
Html.li [] [ Html.text error ]
|
||||||
|
)
|
||||||
|
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|> Html.ul [ Attr.style "color" "red" ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
### Step 2 - Render the Form's View
|
||||||
|
|
||||||
|
view maybeUrl sharedModel app =
|
||||||
|
{ title = "Sign Up"
|
||||||
|
, body =
|
||||||
|
[ form
|
||||||
|
|> Form.toDynamicTransition "login"
|
||||||
|
|> Form.renderHtml [] Nothing app ()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### Step 3 - Handle Server-Side Form Submissions
|
||||||
|
|
||||||
|
action : RouteParams -> Request.Parser (DataSource (Response ActionData ErrorPage))
|
||||||
|
action routeParams =
|
||||||
|
Request.formDataWithoutServerValidation [ signupForm ]
|
||||||
|
|> Request.map
|
||||||
|
(\signupResult ->
|
||||||
|
case signupResult of
|
||||||
|
Ok newUser ->
|
||||||
|
newUser
|
||||||
|
|> myCreateUserDataSource
|
||||||
|
|> DataSource.map
|
||||||
|
(\() ->
|
||||||
|
-- redirect to the home page
|
||||||
|
-- after successful sign-up
|
||||||
|
Route.redirectTo Route.Index
|
||||||
|
)
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
Route.redirectTo Route.Login
|
||||||
|
|> DataSource.succeed
|
||||||
|
)
|
||||||
|
|
||||||
|
myCreateUserDataSource : DataSource ()
|
||||||
|
myCreateUserDataSource =
|
||||||
|
DataSource.fail
|
||||||
|
"TODO - make a database call to create a new user"
|
||||||
|
|
||||||
|
|
||||||
## Building a Form Parser
|
## Building a Form Parser
|
||||||
|
Loading…
Reference in New Issue
Block a user