mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-12-26 05:13:24 +03:00
Wire up Err for client-side validations.
This commit is contained in:
parent
c747893453
commit
e22d688920
@ -188,7 +188,7 @@ page =
|
||||
|
||||
|
||||
type alias Data =
|
||||
{ user : Maybe User
|
||||
{ user : Maybe (Result String User)
|
||||
, errors : Maybe (Dict String { raw : Maybe String, errors : List String })
|
||||
}
|
||||
|
||||
@ -255,21 +255,25 @@ view maybeUrl sharedModel static =
|
||||
user : User
|
||||
user =
|
||||
static.data.user
|
||||
|> Maybe.withDefault defaultUser
|
||||
|> Maybe.withDefault (Ok defaultUser)
|
||||
|> Result.withDefault defaultUser
|
||||
in
|
||||
{ title = "Form Example"
|
||||
, body =
|
||||
[ static.data.user
|
||||
|> Maybe.map
|
||||
(\user_ ->
|
||||
Html.p
|
||||
[ Attr.style "padding" "10px"
|
||||
, Attr.style "background-color" "#a3fba3"
|
||||
]
|
||||
[ Html.text <| "Successfully received user " ++ user_.first ++ " " ++ user_.last
|
||||
]
|
||||
(Result.map
|
||||
(\user_ ->
|
||||
Html.p
|
||||
[ Attr.style "padding" "10px"
|
||||
, Attr.style "background-color" "#a3fba3"
|
||||
]
|
||||
[ Html.text <| "Successfully received user " ++ user_.first ++ " " ++ user_.last
|
||||
]
|
||||
)
|
||||
)
|
||||
|> Maybe.withDefault (Html.p [] [])
|
||||
|> Maybe.withDefault (Err "")
|
||||
|> Result.withDefault (Html.p [] [])
|
||||
, Html.h1
|
||||
[]
|
||||
[ Html.text <| "Edit profile " ++ user.first ++ " " ++ user.last ]
|
||||
|
@ -420,7 +420,7 @@ init _ _ static =
|
||||
|
||||
|
||||
type alias Data =
|
||||
{ user : Maybe User
|
||||
{ user : Maybe (Result String User)
|
||||
, errors : Maybe (Dict String { raw : Maybe String, errors : List String })
|
||||
}
|
||||
|
||||
@ -533,7 +533,8 @@ view maybeUrl sharedModel model static =
|
||||
user : User
|
||||
user =
|
||||
static.data.user
|
||||
|> Maybe.withDefault defaultUser
|
||||
|> Maybe.withDefault (Ok defaultUser)
|
||||
|> Result.withDefault defaultUser
|
||||
in
|
||||
{ title = "Form Example"
|
||||
, body =
|
||||
@ -543,17 +544,20 @@ view maybeUrl sharedModel model static =
|
||||
, formModelView model.form
|
||||
, static.data.user
|
||||
|> Maybe.map
|
||||
(\user_ ->
|
||||
Html.p
|
||||
[ css
|
||||
[ Css.backgroundColor (Css.rgb 163 251 163)
|
||||
, Tw.p_4
|
||||
(Result.map
|
||||
(\user_ ->
|
||||
Html.p
|
||||
[ css
|
||||
[ Css.backgroundColor (Css.rgb 163 251 163)
|
||||
, Tw.p_4
|
||||
]
|
||||
]
|
||||
]
|
||||
[ Html.text <| "Successfully received user " ++ user_.first ++ " " ++ user_.last
|
||||
]
|
||||
[ Html.text <| "Successfully received user " ++ user_.first ++ " " ++ user_.last
|
||||
]
|
||||
)
|
||||
)
|
||||
|> Maybe.withDefault (Html.p [] [])
|
||||
|> Maybe.withDefault (Err "")
|
||||
|> Result.withDefault (Html.p [] [])
|
||||
, Html.div
|
||||
[ css
|
||||
[ Tw.flex
|
||||
|
54
src/Form.elm
54
src/Form.elm
@ -13,12 +13,14 @@ import Server.Request as Request exposing (Request)
|
||||
|
||||
type Form value view
|
||||
= Form
|
||||
-- TODO either make this a Dict and include the client-side validations here
|
||||
-- OR create a new Dict with ( name => client-side validation ( name -> Result String () )
|
||||
(List
|
||||
( List (FieldInfoSimple view)
|
||||
, List view -> List view
|
||||
)
|
||||
)
|
||||
(Request value)
|
||||
(Request (Result String value))
|
||||
(Request
|
||||
(DataSource
|
||||
(List
|
||||
@ -48,6 +50,7 @@ type alias FieldInfoSimple view =
|
||||
-> Maybe { raw : Maybe String, errors : List String }
|
||||
-> view
|
||||
, properties : List ( String, Encode.Value )
|
||||
, clientValidations : Maybe String -> Result String ()
|
||||
}
|
||||
|
||||
|
||||
@ -61,7 +64,7 @@ type alias FieldInfo value view =
|
||||
FinalFieldInfo
|
||||
-> Maybe { raw : Maybe String, errors : List String }
|
||||
-> view
|
||||
, decode : Maybe String -> value
|
||||
, decode : Maybe String -> Result String value
|
||||
, properties : List ( String, Encode.Value )
|
||||
}
|
||||
|
||||
@ -79,7 +82,7 @@ type alias FinalFieldInfo =
|
||||
succeed : constructor -> Form constructor view
|
||||
succeed constructor =
|
||||
Form []
|
||||
(Request.succeed constructor)
|
||||
(Request.succeed (Ok constructor))
|
||||
(Request.succeed (DataSource.succeed []))
|
||||
(\_ -> Ok constructor)
|
||||
|
||||
@ -283,7 +286,7 @@ text name toHtmlFn =
|
||||
toHtmlFn (toInputRecord name Nothing info fieldInfo)
|
||||
|
||||
-- TODO should it be Err if Nothing?
|
||||
, decode = Maybe.withDefault ""
|
||||
, decode = Maybe.withDefault "" >> Ok
|
||||
, properties = []
|
||||
}
|
||||
|
||||
@ -308,7 +311,7 @@ hidden name value toHtmlFn =
|
||||
toHtmlFn (toInputRecord name Nothing info fieldInfo |> .toInput)
|
||||
|
||||
-- TODO should it be Err if Nothing?
|
||||
, decode = Maybe.withDefault ""
|
||||
, decode = Maybe.withDefault "" >> Ok
|
||||
, properties = []
|
||||
}
|
||||
|
||||
@ -375,6 +378,7 @@ radio name nonEmptyItemMapping toHtmlFn wrapFn =
|
||||
\raw ->
|
||||
raw
|
||||
|> Maybe.andThen fromString
|
||||
|> Ok
|
||||
, properties = []
|
||||
}
|
||||
|
||||
@ -414,7 +418,7 @@ submit toHtmlFn =
|
||||
{ attrs =
|
||||
[ Attr.type_ "submit" ]
|
||||
}
|
||||
, decode = \_ -> ()
|
||||
, decode = \_ -> Ok ()
|
||||
, properties = []
|
||||
}
|
||||
|
||||
@ -432,7 +436,7 @@ view viewFn =
|
||||
, toHtml =
|
||||
\fieldInfo info ->
|
||||
viewFn
|
||||
, decode = \_ -> ()
|
||||
, decode = \_ -> Ok ()
|
||||
, properties = []
|
||||
}
|
||||
|
||||
@ -461,6 +465,7 @@ number name toHtmlFn =
|
||||
\rawString ->
|
||||
rawString
|
||||
|> Maybe.andThen String.toInt
|
||||
|> Ok
|
||||
, properties = []
|
||||
}
|
||||
|
||||
@ -489,8 +494,8 @@ requiredNumber name toHtmlFn =
|
||||
\rawString ->
|
||||
rawString
|
||||
|> Maybe.andThen String.toInt
|
||||
-- TODO this needs to be a Result that can be decoded
|
||||
|> Maybe.withDefault -1000
|
||||
-- TODO should this be a custom type instead of String error? That way users can customize the error messages
|
||||
|> Result.fromMaybe "Not a valid number"
|
||||
, properties = []
|
||||
}
|
||||
|
||||
@ -520,8 +525,8 @@ date name toHtmlFn =
|
||||
\rawString ->
|
||||
rawString
|
||||
|> Maybe.withDefault ""
|
||||
-- TODO should empty string be decoded into Nothing instead of an error?
|
||||
|> Date.fromIsoString
|
||||
|> Result.withDefault (Date.fromRataDie 0)
|
||||
, properties = []
|
||||
}
|
||||
|
||||
@ -555,7 +560,7 @@ checkbox name initial toHtmlFn =
|
||||
toHtmlFn (toInputRecord name Nothing info fieldInfo)
|
||||
, decode =
|
||||
\rawString ->
|
||||
rawString == Just "on"
|
||||
Ok (rawString == Just "on")
|
||||
, properties = []
|
||||
}
|
||||
|
||||
@ -645,7 +650,14 @@ withServerValidation : (value -> DataSource (List String)) -> Field value view -
|
||||
withServerValidation serverValidation (Field field) =
|
||||
Field
|
||||
{ field
|
||||
| serverValidation = field.decode >> serverValidation
|
||||
| serverValidation =
|
||||
\value ->
|
||||
case value |> field.decode of
|
||||
Ok decoded ->
|
||||
serverValidation decoded
|
||||
|
||||
Err error ->
|
||||
DataSource.fail <| "Could not decode form data: " ++ error
|
||||
}
|
||||
|
||||
|
||||
@ -674,8 +686,10 @@ with (Field field) (Form fields decoder serverValidations modelToValue) =
|
||||
in
|
||||
Form
|
||||
(addField field fields)
|
||||
(decoder
|
||||
|> Request.andMap (Request.optionalFormField_ field.name |> Request.map field.decode)
|
||||
(Request.map2
|
||||
(Result.map2 (|>))
|
||||
(Request.optionalFormField_ field.name |> Request.map field.decode)
|
||||
decoder
|
||||
)
|
||||
thing
|
||||
(\model ->
|
||||
@ -693,8 +707,9 @@ with (Field field) (Form fields decoder serverValidations modelToValue) =
|
||||
Ok okSoFar ->
|
||||
maybeValue
|
||||
|> field.decode
|
||||
|> okSoFar
|
||||
|> Ok
|
||||
-- TODO have a `List String` for field validation errors, too
|
||||
|> Result.mapError List.singleton
|
||||
|> Result.andThen (okSoFar >> Ok)
|
||||
)
|
||||
|
||||
|
||||
@ -724,7 +739,7 @@ appendForm mapFn (Form fields1 decoder1 serverValidations1 modelToValue1) (Form
|
||||
Form
|
||||
-- TODO is this ordering correct?
|
||||
(fields1 ++ fields2)
|
||||
(Request.map2 mapFn decoder1 decoder2)
|
||||
(Request.map2 (Result.map2 mapFn) decoder1 decoder2)
|
||||
(Request.map2
|
||||
(DataSource.map2 (++))
|
||||
serverValidations1
|
||||
@ -786,6 +801,7 @@ simplify2 field =
|
||||
, serverValidation = field.serverValidation
|
||||
, toHtml = field.toHtml
|
||||
, properties = field.properties
|
||||
, clientValidations = \value -> value |> field.decode |> Result.map (\_ -> ())
|
||||
}
|
||||
|
||||
|
||||
@ -838,7 +854,7 @@ toHtml toForm serverValidationErrors (Form fields decoder serverValidations mode
|
||||
)
|
||||
|
||||
|
||||
toRequest : Form value view -> Request value
|
||||
toRequest : Form value view -> Request (Result String value)
|
||||
toRequest (Form fields decoder serverValidations modelToValue) =
|
||||
Request.expectFormPost
|
||||
(\_ ->
|
||||
@ -851,7 +867,7 @@ toRequest2 :
|
||||
->
|
||||
Request
|
||||
(DataSource
|
||||
(Result Model ( value, Model ))
|
||||
(Result Model ( Result String value, Model ))
|
||||
)
|
||||
toRequest2 (Form fields decoder serverValidations modelToValue) =
|
||||
Request.map2
|
||||
|
@ -27,6 +27,19 @@ all =
|
||||
)
|
||||
|> Expect.equal
|
||||
(Ok "Jane")
|
||||
, test "run a single field's validation on blur" <|
|
||||
\() ->
|
||||
Form.succeed identity
|
||||
|> Form.with (Form.date "dob" toInput)
|
||||
|> Form.runClientValidations
|
||||
(Dict.fromList
|
||||
[ ( "dob"
|
||||
, { raw = Just "This is not a valid date", errors = [] }
|
||||
)
|
||||
]
|
||||
)
|
||||
|> Expect.equal
|
||||
(Err [ "Expected a date in ISO 8601 format" ])
|
||||
]
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user