Extract common helper for preparing validation errors and hidden input values in Form module.

This commit is contained in:
Dillon Kearns 2022-07-12 12:25:14 +02:00
parent 625eeb1a37
commit c5d9fe865d
5 changed files with 96 additions and 134 deletions

View File

@ -37,7 +37,7 @@ type alias RouteParams =
type alias ActionData =
{ user : User
{ user : Maybe User
, formResponse : Maybe { fields : List ( String, String ), errors : Dict String (List String) }
}
@ -198,12 +198,12 @@ action routeParams =
(\userResult ->
(case userResult of
Ok user ->
{ user = user
{ user = Just user
, formResponse = Nothing
}
Err error ->
{ user = defaultUser
{ user = Nothing
, formResponse = Just error
}
)
@ -242,7 +242,7 @@ view maybeUrl sharedModel app =
user : User
user =
app.action
|> Maybe.map .user
|> Maybe.andThen .user
|> Maybe.withDefault defaultUser
in
{ title = "Form Example"
@ -253,7 +253,7 @@ view maybeUrl sharedModel app =
|> Html.text
]
, app.action
|> Maybe.map .user
|> Maybe.andThen .user
|> Maybe.map
(\user_ ->
Html.p
@ -274,6 +274,7 @@ view maybeUrl sharedModel app =
, Attr.style "flex-direction" "column"
, Attr.style "gap" "20px"
]
(app.action |> Maybe.andThen .formResponse)
app
defaultUser
]

View File

@ -196,7 +196,11 @@ view maybeUrl sharedModel model static =
, form
|> Form.toDynamicTransition "test1"
|> Form.withGetMethod
|> Form.renderHtml [] static ()
|> Form.renderHtml []
-- TODO pass in server data
Nothing
static
()
, static.data.results
|> Maybe.map resultsView
|> Maybe.withDefault (Html.div [] [])

View File

@ -264,7 +264,11 @@ view maybeUrl sharedModel model static =
, flashView static.data.flashMessage
, form
|> Form.toDynamicTransition "test1"
|> Form.renderHtml [] static ()
|> Form.renderHtml []
-- TODO pass in server data
Nothing
static
()
]
}

View File

@ -340,6 +340,8 @@ view maybeUrl sharedModel model static =
[ Attr.style "display" "inline"
, Attr.style "padding-left" "6px"
]
-- TODO pass in server data
Nothing
static
item.id
]
@ -360,6 +362,10 @@ view maybeUrl sharedModel model static =
)
, createForm
|> Form.toDynamicTransition "test2"
|> Form.renderHtml [] static ()
|> Form.renderHtml []
-- TODO pass in server data
Nothing
static
()
]
}

View File

@ -862,6 +862,11 @@ runOneOfServerSideWithServerValidations rawFormData parsers =
{-| -}
renderHtml :
List (Html.Attribute (Pages.Msg.Msg msg))
->
Maybe
{ fields : List ( String, String )
, errors : Dict String (List error)
}
-> AppContext app
-> data
->
@ -873,8 +878,8 @@ renderHtml :
-> List (Html (Pages.Msg.Msg msg))
)
-> Html (Pages.Msg.Msg msg)
renderHtml attrs app data (FinalForm options a b c) =
Html.Lazy.lazy5 renderHelper attrs options app data (Form a b c)
renderHtml attrs maybe app data (FinalForm options a b c) =
Html.Lazy.lazy6 renderHelper attrs maybe options app data (Form a b c)
{-| -}
@ -968,104 +973,26 @@ renderStyledHtml attrs maybe app data (FinalForm options a b c) =
renderHelper :
List (Html.Attribute (Pages.Msg.Msg msg))
->
Maybe
{ fields : List ( String, String )
, errors : Dict String (List error)
}
-> RenderOptions
-> AppContext app
-> data
-> Form error (Validation error parsed named) data (Context error data -> List (Html (Pages.Msg.Msg msg)))
-> Html (Pages.Msg.Msg msg)
renderHelper attrs options formState data (Form fieldDefinitions parser toInitialValues) =
renderHelper attrs maybe options formState data ((Form fieldDefinitions parser toInitialValues) as form) =
-- TODO Get transition context from `app` so you can check if the current form is being submitted
-- TODO either as a transition or a fetcher? Should be easy enough to check for the `id` on either of those?
let
formId : String
formId =
options.name |> Maybe.withDefault ""
{ formId, hiddenInputs, children } =
helperValues toHiddenInput maybe options formState data form
initialValues : Dict String Form.FieldState
initialValues =
toInitialValues data
|> List.map (Tuple.mapSecond (\value -> { value = value, status = Form.NotVisited }))
|> Dict.fromList
part2 : Dict String Form.FieldState
part2 =
formState.pageFormState
|> Dict.get formId
|> Maybe.withDefault initFormState
|> .fields
fullFormState : Dict String Form.FieldState
fullFormState =
initialValues
|> Dict.union part2
parsed :
{ result : ( Validation error parsed named, Dict String (List error) )
, view : Context error data -> List (Html (Pages.Msg.Msg msg))
, serverValidations : DataSource (List ( String, List error ))
}
parsed =
parser (Just data) thisFormState
merged : Validation error parsed named
merged =
mergeResults parsed
thisFormState : Form.FormState
thisFormState =
formState.pageFormState
|> Dict.get formId
|> Maybe.withDefault Form.init
|> (\state -> { state | fields = fullFormState })
context : Context error data
context =
{ errors =
merged
|> unwrapValidation
|> Tuple.second
|> Errors
, isTransitioning =
case formState.transition of
Just _ ->
-- TODO need to track the form's ID and check that to see if it's *this*
-- form that is submitting
--transition.todo == formId
True
Nothing ->
False
, submitAttempted = thisFormState.submitAttempted
, data = data
}
children =
parsed.view context
hiddenInputs : List (Html (Pages.Msg.Msg msg))
hiddenInputs =
fieldDefinitions
|> List.filterMap
(\( name, fieldDefinition ) ->
case fieldDefinition of
HiddenField ->
Just
(Html.input
[ Attr.name name
, Attr.type_ "hidden"
, Attr.value
(initialValues
|> Dict.get name
|> Maybe.map .value
|> Maybe.withDefault ""
)
]
[]
)
RegularField ->
Nothing
)
toHiddenInput : List (Html.Attribute (Pages.Msg.Msg msg)) -> Html (Pages.Msg.Msg msg)
toHiddenInput hiddenAttrs =
Html.input hiddenAttrs []
in
Html.form
(Form.listeners formId
@ -1116,9 +1043,47 @@ renderStyledHelper :
-> data
-> Form error (Validation error parsed named) data (Context error data -> List (Html.Styled.Html (Pages.Msg.Msg msg)))
-> Html.Styled.Html (Pages.Msg.Msg msg)
renderStyledHelper attrs maybe options formState data (Form fieldDefinitions parser toInitialValues) =
renderStyledHelper attrs maybe options formState data ((Form fieldDefinitions parser toInitialValues) as form) =
-- TODO Get transition context from `app` so you can check if the current form is being submitted
-- TODO either as a transition or a fetcher? Should be easy enough to check for the `id` on either of those?
let
{ formId, hiddenInputs, children } =
helperValues toHiddenInput maybe options formState data form
toHiddenInput : List (Html.Attribute (Pages.Msg.Msg msg)) -> Html.Styled.Html (Pages.Msg.Msg msg)
toHiddenInput hiddenAttrs =
Html.Styled.input (hiddenAttrs |> List.map StyledAttr.fromUnstyled) []
in
Html.Styled.form
((Form.listeners formId |> List.map StyledAttr.fromUnstyled)
++ [ StyledAttr.method (methodToString options.method)
, StyledAttr.novalidate True
, case options.submitStrategy of
FetcherStrategy ->
StyledAttr.fromUnstyled <| Pages.Msg.fetcherOnSubmit formId (isValid parser data)
TransitionStrategy ->
StyledAttr.fromUnstyled <| Pages.Msg.submitIfValid formId (isValid parser data)
]
++ attrs
)
(hiddenInputs ++ children)
helperValues :
(List (Html.Attribute msg) -> view)
->
Maybe
{ fields : List ( String, String )
, errors : Dict String (List error)
}
-> RenderOptions
-> AppContext app
-> data
---> Form error parsed data view
-> Form error (Validation error parsed named) data (Context error data -> List view)
-> { formId : String, hiddenInputs : List view, children : List view }
helperValues toHiddenInput maybe options formState data (Form fieldDefinitions parser toInitialValues) =
let
formId : String
formId =
@ -1149,10 +1114,6 @@ renderStyledHelper attrs maybe options formState data (Form fieldDefinitions par
)
|> .fields
--formState.pageFormState
-- |> Dict.get formId
-- |> Maybe.withDefault initFormState
-- |> .fields
fullFormState : Dict String Form.FieldState
fullFormState =
initialValues
@ -1160,7 +1121,7 @@ renderStyledHelper attrs maybe options formState data (Form fieldDefinitions par
parsed :
{ result : ( Validation error parsed named, Dict String (List error) )
, view : Context error data -> List (Html.Styled.Html (Pages.Msg.Msg msg))
, view : Context error data -> List view
, serverValidations : DataSource (List ( String, List error ))
}
parsed =
@ -1229,47 +1190,33 @@ renderStyledHelper attrs maybe options formState data (Form fieldDefinitions par
children =
parsed.view context
hiddenInputs : List (Html.Styled.Html (Pages.Msg.Msg msg))
hiddenInputs : List view
hiddenInputs =
fieldDefinitions
|> List.filterMap
(\( name, fieldDefinition ) ->
case fieldDefinition of
HiddenField ->
Just
(Html.Styled.input
([ Attr.name name
, Attr.type_ "hidden"
, Attr.value
(initialValues
|> Dict.get name
|> Maybe.map .value
|> Maybe.withDefault ""
)
]
|> List.map StyledAttr.fromUnstyled
)
[]
[ Attr.name name
, Attr.type_ "hidden"
, Attr.value
(initialValues
|> Dict.get name
|> Maybe.map .value
|> Maybe.withDefault ""
)
]
|> toHiddenInput
|> Just
RegularField ->
Nothing
)
in
Html.Styled.form
((Form.listeners formId |> List.map StyledAttr.fromUnstyled)
++ [ StyledAttr.method (methodToString options.method)
, StyledAttr.novalidate True
, case options.submitStrategy of
FetcherStrategy ->
StyledAttr.fromUnstyled <| Pages.Msg.fetcherOnSubmit formId (isValid parser data)
TransitionStrategy ->
StyledAttr.fromUnstyled <| Pages.Msg.submitIfValid formId (isValid parser data)
]
++ attrs
)
(hiddenInputs ++ children)
{ formId = formId
, hiddenInputs = hiddenInputs
, children = children
}
{-| -}