mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-12-25 21:02:33 +03:00
Add form state to track whether submit was attempted and expose in form state.
This commit is contained in:
parent
cb63a023b6
commit
9dc5a92671
@ -181,12 +181,13 @@ view maybeUrl sharedModel model app =
|
||||
app.fetchers
|
||||
|> List.filterMap
|
||||
(\pending ->
|
||||
case FormParser.runOnList pending.payload.fields actionFormDecoder of
|
||||
( Just (SetQuantity itemId addAmount), _ ) ->
|
||||
Just ( uuidToString itemId, addAmount )
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
-- TODO use the latest FormParser API for this example
|
||||
--case FormParser.runOnList pending.payload.fields actionFormDecoder of
|
||||
-- ( Just (SetQuantity itemId addAmount), _ ) ->
|
||||
-- Just ( uuidToString itemId, addAmount )
|
||||
--
|
||||
-- _ ->
|
||||
Nothing
|
||||
)
|
||||
|> Dict.fromList
|
||||
|
||||
|
@ -147,7 +147,7 @@ form =
|
||||
|> Maybe.withDefault []
|
||||
|
||||
errorsView field =
|
||||
(if field.status == Pages.Form.Blurred || True then
|
||||
(if field.status == Pages.Form.Blurred then
|
||||
field
|
||||
|> errors
|
||||
|> List.map (\error -> Html.li [] [ Html.text error ])
|
||||
@ -179,7 +179,21 @@ form =
|
||||
)
|
||||
)
|
||||
|> FormParser.field "name" (Field.text |> Field.required "Required")
|
||||
|> FormParser.field "description" (Field.text |> Field.required "Required")
|
||||
|> FormParser.field "description"
|
||||
(Field.text
|
||||
|> Field.required "Required"
|
||||
|> Field.withClientValidation
|
||||
(\description ->
|
||||
( Just description
|
||||
, if (description |> String.length) < 5 then
|
||||
[ "Description must be at last 5 characters"
|
||||
]
|
||||
|
||||
else
|
||||
[]
|
||||
)
|
||||
)
|
||||
)
|
||||
|> FormParser.field "price" (Field.int { invalid = \_ -> "Invalid int" } |> Field.required "Required")
|
||||
|> FormParser.field "imageUrl" (Field.text |> Field.required "Required")
|
||||
|
||||
@ -196,7 +210,7 @@ view maybeUrl sharedModel model app =
|
||||
pendingCreation =
|
||||
form
|
||||
|> FormParser.runNew
|
||||
(app.pageFormState |> Dict.get "test" |> Maybe.withDefault Dict.empty)
|
||||
(app.pageFormState |> Dict.get "test" |> Maybe.withDefault Pages.Form.init)
|
||||
|> .result
|
||||
|> parseIgnoreErrors
|
||||
in
|
||||
|
@ -167,15 +167,15 @@ form =
|
||||
, imageUrl = imageUrl.value
|
||||
}
|
||||
)
|
||||
(\info name description price imageUrl ->
|
||||
(\formState name description price imageUrl ->
|
||||
let
|
||||
errors field =
|
||||
info.errors
|
||||
formState.errors
|
||||
|> Dict.get field.name
|
||||
|> Maybe.withDefault []
|
||||
|
||||
errorsView field =
|
||||
(if field.status == Pages.Form.Blurred || True then
|
||||
(if formState.submitAttempted then
|
||||
field
|
||||
|> errors
|
||||
|> List.map (\error -> Html.li [] [ Html.text error ])
|
||||
@ -202,7 +202,15 @@ form =
|
||||
, fieldView "Description" description
|
||||
, fieldView "Price" price
|
||||
, fieldView "Image" imageUrl
|
||||
, Html.button [] [ Html.text "Update" ]
|
||||
, Html.button []
|
||||
[ Html.text
|
||||
(if formState.isTransitioning then
|
||||
"Updating..."
|
||||
|
||||
else
|
||||
"Update"
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
)
|
||||
@ -250,7 +258,7 @@ view maybeUrl sharedModel model app =
|
||||
pendingCreation =
|
||||
form
|
||||
|> FormParser.runNew
|
||||
(app.pageFormState |> Dict.get "test" |> Maybe.withDefault Dict.empty)
|
||||
(app.pageFormState |> Dict.get "test" |> Maybe.withDefault Pages.Form.init)
|
||||
|> .result
|
||||
|> parseIgnoreErrors
|
||||
in
|
||||
|
@ -80,7 +80,7 @@ update eventObject pageFormState =
|
||||
previousValue : FormState
|
||||
previousValue =
|
||||
previousValue_
|
||||
|> Maybe.withDefault Dict.empty
|
||||
|> Maybe.withDefault init
|
||||
in
|
||||
previousValue
|
||||
|> updateForm fieldEvent
|
||||
@ -100,55 +100,84 @@ setField info pageFormState =
|
||||
previousValue : FormState
|
||||
previousValue =
|
||||
previousValue_
|
||||
|> Maybe.withDefault Dict.empty
|
||||
|> Maybe.withDefault init
|
||||
in
|
||||
previousValue
|
||||
|> Dict.update info.name
|
||||
(\previousFieldValue_ ->
|
||||
let
|
||||
previousFieldValue : FieldState
|
||||
previousFieldValue =
|
||||
previousFieldValue_
|
||||
|> Maybe.withDefault { value = "", status = NotVisited }
|
||||
in
|
||||
{ previousFieldValue | value = info.value }
|
||||
|> Just
|
||||
)
|
||||
{ previousValue
|
||||
| fields =
|
||||
previousValue.fields
|
||||
|> Dict.update info.name
|
||||
(\previousFieldValue_ ->
|
||||
let
|
||||
previousFieldValue : FieldState
|
||||
previousFieldValue =
|
||||
previousFieldValue_
|
||||
|> Maybe.withDefault { value = "", status = NotVisited }
|
||||
in
|
||||
{ previousFieldValue | value = info.value }
|
||||
|> Just
|
||||
)
|
||||
}
|
||||
|> Just
|
||||
)
|
||||
|
||||
|
||||
updateForm : FieldEvent -> FormState -> FormState
|
||||
updateForm fieldEvent formState =
|
||||
formState
|
||||
|> Dict.update fieldEvent.name
|
||||
(\previousValue_ ->
|
||||
let
|
||||
previousValue : FieldState
|
||||
previousValue =
|
||||
previousValue_
|
||||
|> Maybe.withDefault { value = fieldEvent.value, status = NotVisited }
|
||||
in
|
||||
(case fieldEvent.event of
|
||||
InputEvent newValue ->
|
||||
{ previousValue | value = newValue }
|
||||
{ formState
|
||||
| fields =
|
||||
formState.fields
|
||||
|> Dict.update fieldEvent.name
|
||||
(\previousValue_ ->
|
||||
let
|
||||
previousValue : FieldState
|
||||
previousValue =
|
||||
previousValue_
|
||||
|> Maybe.withDefault { value = fieldEvent.value, status = NotVisited }
|
||||
in
|
||||
(case fieldEvent.event of
|
||||
InputEvent newValue ->
|
||||
{ previousValue | value = newValue }
|
||||
|
||||
FocusEvent ->
|
||||
{ previousValue | status = previousValue.status |> increaseStatusTo Focused }
|
||||
FocusEvent ->
|
||||
{ previousValue | status = previousValue.status |> increaseStatusTo Focused }
|
||||
|
||||
BlurEvent ->
|
||||
{ previousValue | status = previousValue.status |> increaseStatusTo Blurred }
|
||||
)
|
||||
|> Just
|
||||
BlurEvent ->
|
||||
{ previousValue | status = previousValue.status |> increaseStatusTo Blurred }
|
||||
)
|
||||
|> Just
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
setSubmitAttempted : String -> PageFormState -> PageFormState
|
||||
setSubmitAttempted fieldId pageFormState =
|
||||
pageFormState
|
||||
|> Dict.update fieldId
|
||||
(\maybeForm ->
|
||||
case maybeForm of
|
||||
Just formState ->
|
||||
Just { formState | submitAttempted = True }
|
||||
|
||||
Nothing ->
|
||||
Just { init | submitAttempted = True }
|
||||
)
|
||||
|
||||
|
||||
init : FormState
|
||||
init =
|
||||
{ fields = Dict.empty
|
||||
, submitAttempted = False
|
||||
}
|
||||
|
||||
|
||||
type alias PageFormState =
|
||||
Dict String FormState
|
||||
|
||||
|
||||
type alias FormState =
|
||||
Dict String FieldState
|
||||
{ fields : Dict String FieldState
|
||||
, submitAttempted : Bool
|
||||
}
|
||||
|
||||
|
||||
type alias FieldState =
|
||||
|
@ -1,6 +1,7 @@
|
||||
module Pages.FormParser exposing (..)
|
||||
|
||||
import Dict exposing (Dict)
|
||||
import Dict.Extra
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attr
|
||||
import Html.Lazy
|
||||
@ -25,18 +26,22 @@ type Parser error decoded
|
||||
optional : String -> Parser error (Maybe String)
|
||||
optional name =
|
||||
(\errors form ->
|
||||
( Just (form |> Dict.get name |> Maybe.map .value), errors )
|
||||
( Just (form.fields |> Dict.get name |> Maybe.map .value), errors )
|
||||
)
|
||||
|> Parser
|
||||
|
||||
|
||||
init : Form.FormState
|
||||
init =
|
||||
Debug.todo ""
|
||||
{ fields = Dict.empty
|
||||
, submitAttempted = False
|
||||
}
|
||||
|
||||
|
||||
type alias Context error =
|
||||
{ errors : FieldErrors error
|
||||
, isTransitioning : Bool
|
||||
, submitAttempted : Bool
|
||||
}
|
||||
|
||||
|
||||
@ -65,7 +70,7 @@ field name (Field fieldParser) (CombinedParser definitions parseFn toInitialValu
|
||||
let
|
||||
--something : ( Maybe parsed, List error )
|
||||
( maybeParsed, errors ) =
|
||||
fieldParser.decode (Dict.get name formState |> Maybe.map .value)
|
||||
fieldParser.decode (Dict.get name formState.fields |> Maybe.map .value)
|
||||
|
||||
parsedField : Maybe (ParsedField error parsed)
|
||||
parsedField =
|
||||
@ -80,7 +85,7 @@ field name (Field fieldParser) (CombinedParser definitions parseFn toInitialValu
|
||||
|
||||
rawField : RawField
|
||||
rawField =
|
||||
case formState |> Dict.get name of
|
||||
case formState.fields |> Dict.get name of
|
||||
Just info ->
|
||||
{ name = name
|
||||
, value = Just info.value
|
||||
@ -206,6 +211,7 @@ runNew formState (CombinedParser fieldDefinitions parser _) =
|
||||
{ errors =
|
||||
parsed.result |> Tuple.second
|
||||
, isTransitioning = False
|
||||
, submitAttempted = formState.submitAttempted
|
||||
}
|
||||
in
|
||||
{ result = parsed.result
|
||||
@ -258,7 +264,8 @@ renderHelper formState (CombinedParser fieldDefinitions parser toInitialValues)
|
||||
part2 =
|
||||
formState.pageFormState
|
||||
|> Dict.get formId
|
||||
|> Maybe.withDefault Dict.empty
|
||||
|> Maybe.withDefault init
|
||||
|> .fields
|
||||
|
||||
fullFormState : Dict String Form.FieldState
|
||||
fullFormState =
|
||||
@ -270,7 +277,14 @@ renderHelper formState (CombinedParser fieldDefinitions parser toInitialValues)
|
||||
, view : Context error -> ( List (Html.Attribute (Pages.Msg.Msg msg)), List (Html (Pages.Msg.Msg msg)) )
|
||||
}
|
||||
parsed =
|
||||
parser fullFormState
|
||||
parser thisFormState
|
||||
|
||||
thisFormState : Form.FormState
|
||||
thisFormState =
|
||||
formState.pageFormState
|
||||
|> Dict.get formId
|
||||
|> Maybe.withDefault Form.init
|
||||
|> (\state -> { state | fields = fullFormState })
|
||||
|
||||
context =
|
||||
{ errors =
|
||||
@ -285,6 +299,7 @@ renderHelper formState (CombinedParser fieldDefinitions parser toInitialValues)
|
||||
|
||||
Nothing ->
|
||||
False
|
||||
, submitAttempted = thisFormState.submitAttempted
|
||||
}
|
||||
|
||||
( formAttributes, children ) =
|
||||
@ -294,13 +309,51 @@ renderHelper formState (CombinedParser fieldDefinitions parser toInitialValues)
|
||||
(Form.listeners formId
|
||||
++ [ -- TODO remove hardcoded method - make it part of the config for the form? Should the default be POST?
|
||||
Attr.method "POST"
|
||||
, Pages.Msg.onSubmit
|
||||
, Pages.Msg.submitIfValid
|
||||
(\fields ->
|
||||
case
|
||||
{ init
|
||||
| fields =
|
||||
fields
|
||||
|> List.map (Tuple.mapSecond (\value -> { value = value, status = Form.NotVisited }))
|
||||
|> Dict.fromList
|
||||
}
|
||||
|> parser
|
||||
|> .result
|
||||
|> toResult
|
||||
of
|
||||
Ok _ ->
|
||||
True
|
||||
|
||||
Err _ ->
|
||||
False
|
||||
)
|
||||
]
|
||||
++ formAttributes
|
||||
)
|
||||
children
|
||||
|
||||
|
||||
toResult : ( Maybe parsed, FieldErrors error ) -> Result (FieldErrors error) parsed
|
||||
toResult ( maybeParsed, fieldErrors ) =
|
||||
let
|
||||
isEmptyDict : Bool
|
||||
isEmptyDict =
|
||||
if Dict.isEmpty fieldErrors then
|
||||
True
|
||||
|
||||
else
|
||||
fieldErrors
|
||||
|> Dict.Extra.any (\_ errors -> List.isEmpty errors)
|
||||
in
|
||||
case ( maybeParsed, isEmptyDict ) of
|
||||
( Just parsed, True ) ->
|
||||
Ok parsed
|
||||
|
||||
_ ->
|
||||
Err fieldErrors
|
||||
|
||||
|
||||
render :
|
||||
AppContext app data
|
||||
-> CombinedParser error parsed data (Context error -> view)
|
||||
@ -319,11 +372,13 @@ render formState (CombinedParser fieldDefinitions parser toInitialValues) =
|
||||
, view : Context error -> view
|
||||
}
|
||||
parsed =
|
||||
parser
|
||||
(formState.pageFormState
|
||||
|> Dict.get formId
|
||||
|> Maybe.withDefault Dict.empty
|
||||
)
|
||||
parser thisFormState
|
||||
|
||||
thisFormState : Form.FormState
|
||||
thisFormState =
|
||||
formState.pageFormState
|
||||
|> Dict.get formId
|
||||
|> Maybe.withDefault Form.init
|
||||
|
||||
context =
|
||||
{ errors =
|
||||
@ -339,6 +394,7 @@ render formState (CombinedParser fieldDefinitions parser toInitialValues) =
|
||||
--True
|
||||
Nothing ->
|
||||
False
|
||||
, submitAttempted = thisFormState.submitAttempted
|
||||
}
|
||||
in
|
||||
parsed.view context
|
||||
@ -398,7 +454,7 @@ withError _ _ =
|
||||
required : String -> error -> Parser error String
|
||||
required name error =
|
||||
(\errors form ->
|
||||
case form |> Dict.get name |> Maybe.map .value of
|
||||
case form.fields |> Dict.get name |> Maybe.map .value of
|
||||
Just "" ->
|
||||
( Just "", errors |> addError name error )
|
||||
|
||||
@ -414,7 +470,7 @@ required name error =
|
||||
int : String -> error -> Parser error Int
|
||||
int name error =
|
||||
(\errors form ->
|
||||
case form |> Dict.get name |> Maybe.map .value of
|
||||
case form.fields |> Dict.get name |> Maybe.map .value of
|
||||
Just "" ->
|
||||
( Nothing, errors |> addError name error )
|
||||
|
||||
@ -543,14 +599,15 @@ run formState (Parser parser) =
|
||||
parser Dict.empty formState
|
||||
|
||||
|
||||
runOnList : List ( String, String ) -> Parser error decoded -> ( Maybe decoded, Dict String (List error) )
|
||||
runOnList rawFormData (Parser parser) =
|
||||
(rawFormData
|
||||
|> List.map
|
||||
(Tuple.mapSecond (\value_ -> { value = value_, status = Form.NotVisited }))
|
||||
|> Dict.fromList
|
||||
)
|
||||
|> parser Dict.empty
|
||||
|
||||
--runOnList : List ( String, String ) -> Parser error decoded -> ( Maybe decoded, Dict String (List error) )
|
||||
--runOnList rawFormData (Parser parser) =
|
||||
-- (rawFormData
|
||||
-- |> List.map
|
||||
-- (Tuple.mapSecond (\value_ -> { value = value_, status = Form.NotVisited }))
|
||||
-- |> Dict.fromList
|
||||
-- )
|
||||
-- |> parser Dict.empty
|
||||
|
||||
|
||||
addError : String -> error -> Dict String (List error) -> Dict String (List error)
|
||||
|
@ -454,6 +454,30 @@ update config appMsg model =
|
||||
, Submit fields
|
||||
)
|
||||
|
||||
Pages.Msg.SubmitIfValid fields isValid ->
|
||||
if isValid then
|
||||
( { model
|
||||
| transition =
|
||||
Just
|
||||
( -- TODO remove hardcoded number
|
||||
-1
|
||||
, Pages.Transition.Submitting fields
|
||||
)
|
||||
}
|
||||
, Submit fields
|
||||
)
|
||||
|
||||
else
|
||||
( { model
|
||||
| pageFormState =
|
||||
model.pageFormState
|
||||
|> Pages.Form.setSubmitAttempted
|
||||
-- TODO remove hardcoded fieldId
|
||||
"test"
|
||||
}
|
||||
, NoEffect
|
||||
)
|
||||
|
||||
Pages.Msg.SubmitFetcher fields ->
|
||||
( model
|
||||
, SubmitFetcher fields
|
||||
|
@ -1,6 +1,7 @@
|
||||
module Pages.Msg exposing
|
||||
( Msg(..)
|
||||
, map, onSubmit, fetcherOnSubmit
|
||||
, submitIfValid
|
||||
)
|
||||
|
||||
{-|
|
||||
@ -21,6 +22,7 @@ import Json.Decode
|
||||
type Msg userMsg
|
||||
= UserMsg userMsg
|
||||
| Submit FormDecoder.FormData
|
||||
| SubmitIfValid FormDecoder.FormData Bool
|
||||
| SubmitFetcher FormDecoder.FormData
|
||||
| FormFieldEvent Json.Decode.Value
|
||||
|
||||
@ -32,6 +34,13 @@ onSubmit =
|
||||
|> Html.Attributes.map Submit
|
||||
|
||||
|
||||
{-| -}
|
||||
submitIfValid : (List ( String, String ) -> Bool) -> Attribute (Msg userMsg)
|
||||
submitIfValid isValid =
|
||||
FormDecoder.formDataOnSubmit
|
||||
|> Html.Attributes.map (\formData -> SubmitIfValid formData (isValid formData.fields))
|
||||
|
||||
|
||||
{-| -}
|
||||
fetcherOnSubmit : Attribute (Msg userMsg)
|
||||
fetcherOnSubmit =
|
||||
@ -49,6 +58,9 @@ map mapFn msg =
|
||||
Submit info ->
|
||||
Submit info
|
||||
|
||||
SubmitIfValid info isValid ->
|
||||
SubmitIfValid info isValid
|
||||
|
||||
SubmitFetcher info ->
|
||||
SubmitFetcher info
|
||||
|
||||
|
@ -911,11 +911,13 @@ formParser formParser_ =
|
||||
--something : ( Maybe decoded, Dict String (List String) )
|
||||
( maybeDecoded, errors ) =
|
||||
Pages.FormParser.run
|
||||
(rawFormData
|
||||
|> List.map
|
||||
(Tuple.mapSecond (\value -> { value = value, status = Pages.Form.NotVisited }))
|
||||
|> Dict.fromList
|
||||
)
|
||||
{ fields =
|
||||
rawFormData
|
||||
|> List.map
|
||||
(Tuple.mapSecond (\value -> { value = value, status = Pages.Form.NotVisited }))
|
||||
|> Dict.fromList
|
||||
, submitAttempted = False
|
||||
}
|
||||
formParser_
|
||||
in
|
||||
case ( maybeDecoded, errors |> Dict.toList |> List.NonEmpty.fromList ) of
|
||||
@ -942,11 +944,13 @@ formParserResult formParser_ =
|
||||
--something : ( Maybe decoded, Dict String (List String) )
|
||||
( maybeDecoded, errors ) =
|
||||
Pages.FormParser.run
|
||||
(rawFormData
|
||||
|> List.map
|
||||
(Tuple.mapSecond (\value -> { value = value, status = Pages.Form.NotVisited }))
|
||||
|> Dict.fromList
|
||||
)
|
||||
{ fields =
|
||||
rawFormData
|
||||
|> List.map
|
||||
(Tuple.mapSecond (\value -> { value = value, status = Pages.Form.NotVisited }))
|
||||
|> Dict.fromList
|
||||
, submitAttempted = False
|
||||
}
|
||||
formParser_
|
||||
in
|
||||
case ( maybeDecoded, errors |> Dict.toList |> List.NonEmpty.fromList ) of
|
||||
@ -977,17 +981,19 @@ formParserResultNew formParser_ =
|
||||
let
|
||||
( maybeDecoded, errors ) =
|
||||
Pages.FormParser.runNew
|
||||
(rawFormData
|
||||
|> List.map
|
||||
(Tuple.mapSecond
|
||||
(\value ->
|
||||
{ value = value
|
||||
, status = Pages.Form.NotVisited
|
||||
}
|
||||
{ fields =
|
||||
rawFormData
|
||||
|> List.map
|
||||
(Tuple.mapSecond
|
||||
(\value ->
|
||||
{ value = value
|
||||
, status = Pages.Form.NotVisited
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|> Dict.fromList
|
||||
)
|
||||
|> Dict.fromList
|
||||
, submitAttempted = False
|
||||
}
|
||||
formParser_
|
||||
|> .result
|
||||
in
|
||||
|
Loading…
Reference in New Issue
Block a user