From 00c62a9e96438481ba2a6510f2033bd6c15bdcc6 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Sat, 15 Jan 2022 15:02:32 -0800 Subject: [PATCH] Include form-level errors in server-rendered response. --- examples/pokedex/src/Page/Form.elm | 47 +++++----------- src/Form.elm | 87 ++++++++++++++++++++++-------- 2 files changed, 80 insertions(+), 54 deletions(-) diff --git a/examples/pokedex/src/Page/Form.elm b/examples/pokedex/src/Page/Form.elm index ba82ac4b..c18dfd15 100644 --- a/examples/pokedex/src/Page/Form.elm +++ b/examples/pokedex/src/Page/Form.elm @@ -192,37 +192,25 @@ page = type alias Data = { user : Maybe User - , errors : Maybe Form.ServerUpdate + , errors : Form.Model } data : RouteParams -> Request (DataSource (PageServerResponse Data)) data routeParams = Request.oneOf - [ Form.toRequest2 (form defaultUser) - |> Request.map - (\userOrErrors -> - userOrErrors - |> DataSource.map - (\result -> - (case result of - Ok ( errors, user ) -> - { user = Just user - , errors = Just errors - } - - Err errors -> - { user = Nothing - , errors = Just errors - } - ) - |> PageServerResponse.RenderPage - ) - ) - , PageServerResponse.RenderPage - { user = Nothing - , errors = Nothing - } + [ Form.submitHandlers + (form defaultUser) + (\model decoded -> + DataSource.succeed + { user = Result.toMaybe decoded + , errors = model + } + ) + , { user = Nothing + , errors = Form.init (form defaultUser) + } + |> PageServerResponse.RenderPage |> DataSource.succeed |> Request.succeed ] @@ -277,14 +265,7 @@ view maybeUrl sharedModel static = [] [ Html.text <| "Edit profile " ++ user.first ++ " " ++ user.last ] , form user - |> Form.toHtml { pageReloadSubmit = True } - Html.form - { fields = - static.data.errors - |> Maybe.withDefault (Form.init (form defaultUser) |> .fields) - , isSubmitting = Form.NotSubmitted - , formErrors = Dict.empty - } + |> Form.toHtml { pageReloadSubmit = True } Html.form static.data.errors |> Html.map (\_ -> ()) ] } diff --git a/src/Form.elm b/src/Form.elm index ef54293c..068e3bed 100644 --- a/src/Form.elm +++ b/src/Form.elm @@ -75,6 +75,12 @@ errorCodec = Codec.string +type alias RawModel error = + { fields : List ( String, List error ) + , formErrors : Dict String (List error) + } + + type Form error value view = Form -- TODO either make this a Dict and include the client-side validations here @@ -309,12 +315,10 @@ update toMsg onResponse ((Form fields decoder serverValidations modelToValue) as Ok ( decodedModel, errors ) -> errors |> Dict.fromList - |> Debug.log "formErrors Ok" Err errors -> errors |> Dict.fromList - |> Debug.log "formErrors Err" in ( initialModel, Cmd.none ) @@ -430,8 +434,7 @@ init ((Form fields decoder serverValidations modelToValue) as form) = { fields = initialFields , isSubmitting = NotSubmitted , formErrors = - case modelToValue initialFields |> Debug.log "@@@123" of - -- TODO use these errors here + case modelToValue initialFields of Ok ( decodedModel, errors ) -> errors |> Dict.fromList @@ -1714,32 +1717,49 @@ toRequest2 : -> Request (DataSource - (Result (FieldState String) ( FieldState String, value )) + (Result Model ( Model, value )) ) toRequest2 (Form fields decoder serverValidations modelToValue) = Request.map2 (\decoded errors -> errors |> DataSource.map - (\validationErrors -> + (\model -> case decoded of Ok ( value, otherValidationErrors ) -> - if not (hasErrors validationErrors) && (otherValidationErrors |> List.isEmpty) then + --if not (hasErrors validationErrors) && (otherValidationErrors |> List.isEmpty) then + if otherValidationErrors |> List.isEmpty then Ok - ( validationErrors |> Dict.fromList + ( --validationErrors |> Dict.fromList + { model + | fields = + model.fields + |> combineWithErrors otherValidationErrors + } , value ) else - validationErrors - |> Dict.fromList - |> combineWithErrors otherValidationErrors + --validationErrors + -- |> Dict.fromList + -- |> combineWithErrors otherValidationErrors + { model + | fields = + model.fields + |> combineWithErrors otherValidationErrors + } |> Err Err otherValidationErrors -> - validationErrors - |> Dict.fromList - |> combineWithErrors otherValidationErrors + --validationErrors + -- |> Dict.fromList + -- |> combineWithErrors otherValidationErrors + -- |> Err + { model + | fields = + model.fields + |> combineWithErrors otherValidationErrors + } |> Err ) ) @@ -1751,6 +1771,34 @@ toRequest2 (Form fields decoder serverValidations modelToValue) = (Request.expectFormPost (\{ optionalField } -> serverValidations optionalField + |> Request.map + (DataSource.map + (\thing -> + let + fullFieldState : Dict String (RawFieldState String) + fullFieldState = + thing + |> Dict.fromList + + otherErrors : + Result + (List ( String, List String )) + ( value, List ( String, List String ) ) + otherErrors = + modelToValue fullFieldState + in + { fields = fullFieldState + , isSubmitting = Submitted + , formErrors = + case otherErrors of + Ok ( _, okErrors ) -> + okErrors |> Dict.fromList + + Err errErrors -> + errErrors |> Dict.fromList + } + ) + ) ) ) @@ -1778,15 +1826,12 @@ submitHandlers myForm toDataSource = |> DataSource.andThen (\result -> case result of - Ok ( errors, decoded ) -> + Ok ( model, decoded ) -> Ok decoded - |> toDataSource - { fields = errors, isSubmitting = Submitted, formErrors = Dict.empty } + |> toDataSource model - Err errors -> - Err () - |> toDataSource - { fields = errors, isSubmitting = Submitted, formErrors = Dict.empty } + Err model -> + Err () |> toDataSource model ) |> DataSource.map PageServerResponse.RenderPage )