Add way to provide default values from DataSource.

This commit is contained in:
Dillon Kearns 2022-06-12 12:27:10 -07:00
parent 68cfe7019f
commit 156c843926
5 changed files with 101 additions and 58 deletions

View File

@ -6,6 +6,7 @@ import DataSource exposing (DataSource)
import Dict exposing (Dict)
import Effect exposing (Effect)
import ErrorPage exposing (ErrorPage)
import Form.Value
import Head
import Head.Seo as Seo
import Html exposing (Html)
@ -62,18 +63,7 @@ init :
-> ( Model, Effect Msg )
init maybePageUrl sharedModel static =
( {}
, Effect.batch
[ Effect.SetField
{ formId = "test"
, name = "username"
, value = static.data.user.username
}
, Effect.SetField
{ formId = "test"
, name = "name"
, value = static.data.user.name
}
]
, Effect.none
)
@ -127,12 +117,8 @@ type alias Action =
}
newDecoder :
FormParser.CombinedParser
String
{ username : String, name : String }
(FormParser.Context String -> ( List (Html.Attribute msg), List (Html msg) ))
newDecoder =
formParser : FormParser.HtmlForm String { username : String, name : String } Data msg
formParser =
FormParser.andThenNew
(\username name ->
FormParser.ok
@ -186,10 +172,12 @@ newDecoder =
(Field.text
|> Field.required "Username is required"
|> Field.withClientValidation validateUsername
|> Field.withInitialValue (\{ user } -> Form.Value.string user.username)
)
|> FormParser.field "name"
(Field.text
|> Field.required "Name is required"
|> Field.withInitialValue (\{ user } -> Form.Value.string user.name)
)
@ -205,7 +193,7 @@ validateUsername rawUsername =
action : RouteParams -> Request.Parser (DataSource (Response ActionData ErrorPage))
action routeParams =
Request.map2 Tuple.pair
(Request.formParserResultNew newDecoder)
(Request.formParserResultNew formParser)
Request.requestTime
|> MySession.expectSessionDataOrRedirect (Session.get "userId" >> Maybe.map Uuid)
(\userId ( parsedAction, requestTime ) session ->
@ -264,6 +252,6 @@ view maybeUrl sharedModel model app =
]
[ Html.button [ Attr.name "kind", Attr.value "signout" ] [ Html.text "Sign out" ] ]
]
, FormParser.renderHtml app newDecoder
, FormParser.renderHtml app formParser
]
}

View File

@ -1,20 +1,19 @@
module Pages.Field exposing (..)
import DataSource exposing (DataSource)
import Form.Value
import Json.Encode as Encode
type Field error parsed constraints
= Field (FieldInfo error parsed)
type Field error parsed data constraints
= Field (FieldInfo error parsed data)
type alias FieldInfo error parsed =
{ --, initialValue : Maybe String
type_ : String
type alias FieldInfo error parsed data =
{ initialValue : Maybe (data -> String)
, type_ : String
, required : Bool
, serverValidation : Maybe String -> DataSource (List error)
--, decode : String -> Form.FormState -> ( Maybe parsed, List error )
, decode : Maybe String -> ( Maybe parsed, List error )
, properties : List ( String, Encode.Value )
}
@ -37,14 +36,16 @@ required :
Field
error
(Maybe parsed)
data
{ constraints
| required : ()
, wasMapped : No
}
-> Field error parsed { constraints | wasMapped : No }
-> Field error parsed data { constraints | wasMapped : No }
required missingError (Field field) =
Field
{ type_ = field.type_
{ initialValue = field.initialValue
, type_ = field.type_
, required = True
, serverValidation = field.serverValidation
, decode =
@ -64,6 +65,7 @@ text :
Field
error
(Maybe String)
data
{ required : ()
, plainText : ()
, wasMapped : No
@ -71,7 +73,8 @@ text :
}
text =
Field
{ type_ = "text"
{ initialValue = Nothing
, type_ = "text"
, required = False
, serverValidation = \_ -> DataSource.succeed []
, decode =
@ -92,11 +95,13 @@ checkbox :
Field
error
Bool
data
{ required : ()
}
checkbox =
Field
{ type_ = "checkbox"
{ initialValue = Nothing
, type_ = "checkbox"
, required = False
, serverValidation = \_ -> DataSource.succeed []
, decode =
@ -116,6 +121,7 @@ int :
Field
error
(Maybe Int)
data
{ min : Int
, max : Int
, required : ()
@ -124,7 +130,8 @@ int :
}
int toError =
Field
{ type_ = "number"
{ initialValue = Nothing
, type_ = "number"
, required = False
, serverValidation = \_ -> DataSource.succeed []
, decode =
@ -148,10 +155,11 @@ int toError =
{-| -}
withClientValidation : (parsed -> ( Maybe mapped, List error )) -> Field error parsed constraints -> Field error mapped { constraints | wasMapped : Yes }
withClientValidation : (parsed -> ( Maybe mapped, List error )) -> Field error parsed data constraints -> Field error mapped data { constraints | wasMapped : Yes }
withClientValidation mapFn (Field field) =
Field
{ type_ = field.type_
{ initialValue = field.initialValue
, type_ = field.type_
, required = field.required
, serverValidation = field.serverValidation
, decode =
@ -171,3 +179,13 @@ withClientValidation mapFn (Field field) =
)
, properties = field.properties
}
{-| -}
withInitialValue : (data -> Form.Value.Value valueType) -> Field error value data { constraints | initial : valueType } -> Field error value data constraints
withInitialValue toInitialValue (Field field) =
Field
{ field
| initialValue =
Just (toInitialValue >> Form.Value.toString)
}

View File

@ -25,7 +25,8 @@ type Event
type alias FieldEvent =
{ formId : String
{ value : String
, formId : String
, name : String
, event : Event
}
@ -33,7 +34,8 @@ type alias FieldEvent =
fieldEventDecoder : Decoder FieldEvent
fieldEventDecoder =
Decode.map3 FieldEvent
Decode.map4 FieldEvent
(Decode.at [ "target", "value" ] Decode.string)
(Decode.at [ "currentTarget", "id" ] Decode.string)
(Decode.at [ "target", "name" ] Decode.string)
fieldDecoder
@ -125,7 +127,7 @@ updateForm fieldEvent formState =
previousValue : FieldState
previousValue =
previousValue_
|> Maybe.withDefault { value = "", status = NotVisited }
|> Maybe.withDefault { value = fieldEvent.value, status = NotVisited }
in
(case fieldEvent.event of
InputEvent newValue ->

View File

@ -40,7 +40,7 @@ type alias Context error =
}
andThenNew : combined -> (Context String -> viewFn) -> CombinedParser String combined (Context String -> viewFn)
andThenNew : combined -> (Context String -> viewFn) -> CombinedParser String combined data (Context String -> viewFn)
andThenNew fn viewFn =
CombinedParser []
(\formState ->
@ -48,14 +48,15 @@ andThenNew fn viewFn =
, view = viewFn
}
)
(\_ -> [])
field :
String
-> Field error parsed constraints
-> CombinedParser error (ParsedField error parsed -> combined) (Context error -> (RawField -> combinedView))
-> CombinedParser error combined (Context error -> combinedView)
field name (Field fieldParser) (CombinedParser definitions parseFn) =
-> Field error parsed data constraints
-> CombinedParser error (ParsedField error parsed -> combined) data (Context error -> (RawField -> combinedView))
-> CombinedParser error combined data (Context error -> combinedView)
field name (Field fieldParser) (CombinedParser definitions parseFn toInitialValues) =
CombinedParser
(( name, FieldDefinition )
:: definitions
@ -132,6 +133,15 @@ field name (Field fieldParser) (CombinedParser definitions parseFn) =
|> parseFn
|> myFn
)
(\data ->
case fieldParser.initialValue of
Just toInitialValue ->
( name, toInitialValue data )
:: toInitialValues data
Nothing ->
toInitialValues data
)
type ParsingResult a
@ -157,7 +167,7 @@ type alias FieldErrors error =
Dict String (List error)
type alias AppContext app =
type alias AppContext app data =
{ app
| --, sharedData : Shared.Data
--, routeParams : routeParams
@ -169,17 +179,18 @@ type alias AppContext app =
transition : Maybe Pages.Transition.Transition
, fetchers : List Pages.Transition.FetcherState
, pageFormState : Form.PageFormState
, data : data
}
runNew :
Form.FormState
-> CombinedParser error parsed (Context error -> view)
-> CombinedParser error parsed data (Context error -> view)
->
{ result : ( Maybe parsed, FieldErrors error )
, view : view
}
runNew formState (CombinedParser fieldDefinitions parser) =
runNew formState (CombinedParser fieldDefinitions parser _) =
-- 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
@ -202,11 +213,12 @@ runNew formState (CombinedParser fieldDefinitions parser) =
renderHtml :
AppContext app
AppContext app data
->
CombinedParser
error
parsed
data
(Context error
-> ( List (Html.Attribute (Pages.Msg.Msg msg)), List (Html (Pages.Msg.Msg msg)) )
)
@ -216,16 +228,17 @@ renderHtml formState_ combinedParser =
renderHelper :
AppContext app
AppContext app data
->
CombinedParser
error
parsed
data
(Context error
-> ( List (Html.Attribute (Pages.Msg.Msg msg)), List (Html (Pages.Msg.Msg msg)) )
)
-> Html (Pages.Msg.Msg msg)
renderHelper formState (CombinedParser fieldDefinitions parser) =
renderHelper formState (CombinedParser fieldDefinitions parser toInitialValues) =
-- 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
@ -234,16 +247,29 @@ renderHelper formState (CombinedParser fieldDefinitions parser) =
-- TODO remove hardcoding
"test"
initialValues : Dict String Form.FieldState
initialValues =
toInitialValues formState.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 Dict.empty
fullFormState : Dict String Form.FieldState
fullFormState =
initialValues
|> Dict.union part2
parsed :
{ result : ( Maybe parsed, Dict String (List error) )
, view : Context error -> ( List (Html.Attribute (Pages.Msg.Msg msg)), List (Html (Pages.Msg.Msg msg)) )
}
parsed =
parser
(formState.pageFormState
|> Dict.get formId
|> Maybe.withDefault Dict.empty
)
parser fullFormState
context =
{ errors =
@ -275,10 +301,10 @@ renderHelper formState (CombinedParser fieldDefinitions parser) =
render :
AppContext app
-> CombinedParser error parsed (Context error -> view)
AppContext app data
-> CombinedParser error parsed data (Context error -> view)
-> view
render formState (CombinedParser fieldDefinitions parser) =
render formState (CombinedParser fieldDefinitions parser toInitialValues) =
-- 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
@ -317,7 +343,15 @@ render formState (CombinedParser fieldDefinitions parser) =
parsed.view context
type CombinedParser error parsed view
type alias HtmlForm error parsed data msg =
CombinedParser
error
parsed
data
(Context error -> ( List (Html.Attribute (Pages.Msg.Msg msg)), List (Html (Pages.Msg.Msg msg)) ))
type CombinedParser error parsed data view
= CombinedParser
(List ( String, FieldDefinition ))
(Form.FormState
@ -329,6 +363,7 @@ type CombinedParser error parsed view
, view : view
}
)
(data -> List ( String, String ))
type FieldDefinition

View File

@ -968,7 +968,7 @@ formParserResult formParser_ =
{-| -}
formParserResultNew :
Pages.FormParser.CombinedParser error combined (Pages.FormParser.Context error -> viewFn)
Pages.FormParser.CombinedParser error combined data (Pages.FormParser.Context error -> viewFn)
-> Parser (Result { fields : List ( String, String ), errors : Dict String (List error) } combined)
formParserResultNew formParser_ =
formData