elm-pages 3.0.0-beta.32 → 3.0.0-beta.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/codegen/elm-pages-codegen.cjs +10 -78
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/dead-code-review/elm.json +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/review/elm.json +1 -1
- package/generator/src/RouteBuilder.elm +2 -2
- package/generator/src/compatibility-key.js +2 -2
- package/generator/src/render.js +1 -0
- package/generator/src/request-cache.js +20 -6
- package/generator/static-code/elm-pages.js +10 -0
- package/package.json +3 -3
- package/src/BackendTask/Http.elm +8 -2
- package/src/Internal/Field.elm +19 -0
- package/src/Internal/Input.elm +81 -0
- package/src/Pages/Form.elm +229 -0
- package/src/Pages/Internal/Msg.elm +70 -61
- package/src/Pages/Internal/Platform/Cli.elm +423 -425
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +1 -5
- package/src/Pages/Internal/Platform.elm +119 -100
- package/src/Pages/ProgramConfig.elm +12 -5
- package/src/Pages/StaticHttpRequest.elm +0 -1
- package/src/Pages/Transition.elm +9 -1
- package/src/PagesMsg.elm +0 -10
- package/src/RequestsAndPending.elm +31 -3
- package/src/Scaffold/Form.elm +9 -9
- package/src/Server/Request.elm +57 -61
- package/src/Form/Field.elm +0 -729
- package/src/Form/FieldStatus.elm +0 -36
- package/src/Form/FieldView.elm +0 -497
- package/src/Form/FormData.elm +0 -22
- package/src/Form/Validation.elm +0 -391
- package/src/Form/Value.elm +0 -118
- package/src/Form.elm +0 -1558
- package/src/FormDecoder.elm +0 -102
- package/src/Pages/FormState.elm +0 -257
- package/src/Pages/Internal/Form.elm +0 -37
package/src/Form.elm
DELETED
|
@@ -1,1558 +0,0 @@
|
|
|
1
|
-
module Form exposing
|
|
2
|
-
( Form, HtmlForm, StyledHtmlForm, DoneForm
|
|
3
|
-
, Response
|
|
4
|
-
, init
|
|
5
|
-
, field, hiddenField, hiddenKind
|
|
6
|
-
, Context
|
|
7
|
-
, renderHtml, renderStyledHtml
|
|
8
|
-
, withGetMethod, toDynamicFetcher
|
|
9
|
-
, Errors, errorsForField
|
|
10
|
-
, parse, runServerSide, runOneOfServerSide
|
|
11
|
-
, ServerForms(..)
|
|
12
|
-
, initCombined, combine, initCombinedServer, combineServer
|
|
13
|
-
, dynamic
|
|
14
|
-
, AppContext
|
|
15
|
-
, toServerForm, withOnSubmit
|
|
16
|
-
-- subGroup
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
{-| One of the core features of elm-pages is helping you manage form data end-to-end, including
|
|
20
|
-
|
|
21
|
-
- Presenting the HTML form with its fields
|
|
22
|
-
- Maintaining client-side form state
|
|
23
|
-
- Showing validation errors on the client-side
|
|
24
|
-
- Receiving a form submission on the server-side
|
|
25
|
-
- Using the exact same client-side validations on the server-side
|
|
26
|
-
- Letting you run server-only Validations with BackendTask's (things like checking for a unique username)
|
|
27
|
-
|
|
28
|
-
Because elm-pages is a framework, it has its own internal Model and Msg's. That means you, the user,
|
|
29
|
-
can offload some of the responsibility to elm-pages and build an interactive form with real-time
|
|
30
|
-
client-side state and validation errors without wiring up your own Model and Msg's to manage that
|
|
31
|
-
state. You define the source of truth for your form (how to parse it into data or errors), and
|
|
32
|
-
elm-pages manages the state.
|
|
33
|
-
|
|
34
|
-
Let's look at a sign-up form example.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
### Step 1 - Define the Form
|
|
38
|
-
|
|
39
|
-
What to look for:
|
|
40
|
-
|
|
41
|
-
**The field declarations**
|
|
42
|
-
|
|
43
|
-
Below the `Form.init` call you will find all of the form's fields declared with
|
|
44
|
-
|
|
45
|
-
|> Form.field ...
|
|
46
|
-
|
|
47
|
-
These are the form's field declarations.
|
|
48
|
-
|
|
49
|
-
These fields each have individual validations. For example, `|> Field.required ...` means we'll get a validation
|
|
50
|
-
error if that field is empty (similar for checking the minimum password length).
|
|
51
|
-
|
|
52
|
-
There will be a corresponding parameter in the function we pass in to `Form.init` for every
|
|
53
|
-
field declaration (in this example, `\email password passwordConfirmation -> ...`).
|
|
54
|
-
|
|
55
|
-
**The `combine` validation**
|
|
56
|
-
|
|
57
|
-
In addition to the validation errors that individual fields can have independently (like
|
|
58
|
-
required fields or minimum password length), we can also do _dependent validations_.
|
|
59
|
-
|
|
60
|
-
We use the [`Form.Validation`](Form-Validation) module to take each individual field and combine
|
|
61
|
-
them into a type and/or errors.
|
|
62
|
-
|
|
63
|
-
**The `view`**
|
|
64
|
-
|
|
65
|
-
Totally customizable. Uses [`Form.FieldView`](Form-FieldView) to render all of the fields declared.
|
|
66
|
-
|
|
67
|
-
import BackendTask exposing (BackendTask)
|
|
68
|
-
import ErrorPage exposing (ErrorPage)
|
|
69
|
-
import Form
|
|
70
|
-
import Form.Field as Field
|
|
71
|
-
import Form.FieldView as FieldView
|
|
72
|
-
import Form.Validation as Validation
|
|
73
|
-
import Html exposing (Html)
|
|
74
|
-
import Html.Attributes as Attr
|
|
75
|
-
import Route
|
|
76
|
-
import Server.Request as Request
|
|
77
|
-
import Server.Response exposing (Response)
|
|
78
|
-
|
|
79
|
-
type alias NewUser =
|
|
80
|
-
{ email : String
|
|
81
|
-
, password : String
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
signupForm : Form.HtmlForm String NewUser () Msg
|
|
85
|
-
signupForm =
|
|
86
|
-
Form.init
|
|
87
|
-
(\email password passwordConfirmation ->
|
|
88
|
-
{ combine =
|
|
89
|
-
Validation.succeed Login
|
|
90
|
-
|> Validation.andMap email
|
|
91
|
-
|> Validation.andMap
|
|
92
|
-
(Validation.map2
|
|
93
|
-
(\pass confirmation ->
|
|
94
|
-
if pass == confirmation then
|
|
95
|
-
Validation.succeed pass
|
|
96
|
-
|
|
97
|
-
else
|
|
98
|
-
passwordConfirmation
|
|
99
|
-
|> Validation.fail
|
|
100
|
-
"Must match password"
|
|
101
|
-
)
|
|
102
|
-
password
|
|
103
|
-
passwordConfirmation
|
|
104
|
-
|> Validation.andThen identity
|
|
105
|
-
)
|
|
106
|
-
, view =
|
|
107
|
-
\info ->
|
|
108
|
-
[ Html.label []
|
|
109
|
-
[ fieldView info "Email" email
|
|
110
|
-
, fieldView info "Password" password
|
|
111
|
-
, fieldView info "Confirm Password" passwordConfirmation
|
|
112
|
-
]
|
|
113
|
-
, Html.button []
|
|
114
|
-
[ if info.isTransitioning then
|
|
115
|
-
Html.text "Signing Up..."
|
|
116
|
-
|
|
117
|
-
else
|
|
118
|
-
Html.text "Sign Up"
|
|
119
|
-
]
|
|
120
|
-
]
|
|
121
|
-
}
|
|
122
|
-
)
|
|
123
|
-
|> Form.field "email"
|
|
124
|
-
(Field.text
|
|
125
|
-
|> Field.required "Required"
|
|
126
|
-
)
|
|
127
|
-
|> Form.field "password"
|
|
128
|
-
passwordField
|
|
129
|
-
|> Form.field "passwordConfirmation"
|
|
130
|
-
passwordField
|
|
131
|
-
|
|
132
|
-
passwordField =
|
|
133
|
-
Field.text
|
|
134
|
-
|> Field.password
|
|
135
|
-
|> Field.required "Required"
|
|
136
|
-
|> Field.withClientValidation
|
|
137
|
-
(\password ->
|
|
138
|
-
( Just password
|
|
139
|
-
, if String.length password < 4 then
|
|
140
|
-
[ "Must be at least 4 characters" ]
|
|
141
|
-
|
|
142
|
-
else
|
|
143
|
-
[]
|
|
144
|
-
)
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
fieldView :
|
|
148
|
-
Form.Context String data
|
|
149
|
-
-> String
|
|
150
|
-
-> Validation.Field String parsed FieldView.Input
|
|
151
|
-
-> Html msg
|
|
152
|
-
fieldView formState label field =
|
|
153
|
-
Html.div []
|
|
154
|
-
[ Html.label []
|
|
155
|
-
[ Html.text (label ++ " ")
|
|
156
|
-
, field |> Form.FieldView.input []
|
|
157
|
-
]
|
|
158
|
-
, (if formState.submitAttempted then
|
|
159
|
-
formState.errors
|
|
160
|
-
|> Form.errorsForField field
|
|
161
|
-
|> List.map
|
|
162
|
-
(\error ->
|
|
163
|
-
Html.li [] [ Html.text error ]
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
else
|
|
167
|
-
[]
|
|
168
|
-
)
|
|
169
|
-
|> Html.ul [ Attr.style "color" "red" ]
|
|
170
|
-
]
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
### Step 2 - Render the Form's View
|
|
174
|
-
|
|
175
|
-
view maybeUrl sharedModel app =
|
|
176
|
-
{ title = "Sign Up"
|
|
177
|
-
, body =
|
|
178
|
-
[ form
|
|
179
|
-
|> Form.renderHtml "login" [] Nothing app ()
|
|
180
|
-
]
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
### Step 3 - Handle Server-Side Form Submissions
|
|
185
|
-
|
|
186
|
-
action : RouteParams -> Request.Parser (BackendTask (Response ActionData ErrorPage))
|
|
187
|
-
action routeParams =
|
|
188
|
-
Request.formData [ signupForm ]
|
|
189
|
-
|> Request.map
|
|
190
|
-
(\signupResult ->
|
|
191
|
-
case signupResult of
|
|
192
|
-
Ok newUser ->
|
|
193
|
-
newUser
|
|
194
|
-
|> myCreateUserBackendTask
|
|
195
|
-
|> BackendTask.map
|
|
196
|
-
(\() ->
|
|
197
|
-
-- redirect to the home page
|
|
198
|
-
-- after successful sign-up
|
|
199
|
-
Route.redirectTo Route.Index
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
Err _ ->
|
|
203
|
-
Route.redirectTo Route.Login
|
|
204
|
-
|> BackendTask.succeed
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
myCreateUserBackendTask : BackendTask ()
|
|
208
|
-
myCreateUserBackendTask =
|
|
209
|
-
BackendTask.fail
|
|
210
|
-
"TODO - make a database call to create a new user"
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
## Building a Form Parser
|
|
214
|
-
|
|
215
|
-
@docs Form, HtmlForm, StyledHtmlForm, DoneForm
|
|
216
|
-
|
|
217
|
-
@docs Response
|
|
218
|
-
|
|
219
|
-
@docs init
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
## Adding Fields
|
|
223
|
-
|
|
224
|
-
@docs field, hiddenField, hiddenKind
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
## View Functions
|
|
228
|
-
|
|
229
|
-
@docs Context
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
## Rendering Forms
|
|
233
|
-
|
|
234
|
-
@docs renderHtml, renderStyledHtml
|
|
235
|
-
|
|
236
|
-
@docs withGetMethod, toDynamicFetcher
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
## Showing Errors
|
|
240
|
-
|
|
241
|
-
@docs Errors, errorsForField
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
## Running Parsers
|
|
245
|
-
|
|
246
|
-
@docs parse, runServerSide, runOneOfServerSide
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
## Combining Forms to Run on Server
|
|
250
|
-
|
|
251
|
-
@docs ServerForms
|
|
252
|
-
|
|
253
|
-
@docs initCombined, combine, initCombinedServer, combineServer
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
## Dynamic Fields
|
|
257
|
-
|
|
258
|
-
@docs dynamic
|
|
259
|
-
|
|
260
|
-
@docs AppContext
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
## Submission
|
|
264
|
-
|
|
265
|
-
@docs toServerForm, withOnSubmit
|
|
266
|
-
|
|
267
|
-
-}
|
|
268
|
-
|
|
269
|
-
import BackendTask exposing (BackendTask)
|
|
270
|
-
import Dict exposing (Dict)
|
|
271
|
-
import FatalError exposing (FatalError)
|
|
272
|
-
import Form.Field as Field exposing (Field(..))
|
|
273
|
-
import Form.FieldStatus as FieldStatus exposing (FieldStatus)
|
|
274
|
-
import Form.FieldView
|
|
275
|
-
import Form.Validation exposing (Combined)
|
|
276
|
-
import Html exposing (Html)
|
|
277
|
-
import Html.Attributes as Attr
|
|
278
|
-
import Html.Lazy
|
|
279
|
-
import Html.Styled
|
|
280
|
-
import Html.Styled.Attributes as StyledAttr
|
|
281
|
-
import Html.Styled.Lazy
|
|
282
|
-
import Pages.FormState as Form exposing (FormState)
|
|
283
|
-
import Pages.Internal.Form exposing (Validation(..), unwrapResponse)
|
|
284
|
-
import Pages.Internal.Msg
|
|
285
|
-
import Pages.Transition exposing (Transition(..))
|
|
286
|
-
import PagesMsg exposing (PagesMsg)
|
|
287
|
-
import Path exposing (Path)
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
{-| -}
|
|
291
|
-
initFormState : FormState
|
|
292
|
-
initFormState =
|
|
293
|
-
{ fields = Dict.empty
|
|
294
|
-
, submitAttempted = False
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
{-| -}
|
|
299
|
-
type alias Context error data =
|
|
300
|
-
{ errors : Errors error
|
|
301
|
-
, isTransitioning : Bool
|
|
302
|
-
, submitAttempted : Bool
|
|
303
|
-
, data : data
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
{-| -}
|
|
308
|
-
init : combineAndView -> Form String combineAndView data msg
|
|
309
|
-
init combineAndView =
|
|
310
|
-
Form
|
|
311
|
-
{ submitStrategy = TransitionStrategy
|
|
312
|
-
, method = Post
|
|
313
|
-
, onSubmit = Nothing
|
|
314
|
-
}
|
|
315
|
-
[]
|
|
316
|
-
(\_ _ ->
|
|
317
|
-
{ result = Dict.empty
|
|
318
|
-
, combineAndView = combineAndView
|
|
319
|
-
, isMatchCandidate = True
|
|
320
|
-
}
|
|
321
|
-
)
|
|
322
|
-
(\_ -> [])
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
{-| -}
|
|
326
|
-
dynamic :
|
|
327
|
-
(decider
|
|
328
|
-
->
|
|
329
|
-
Form
|
|
330
|
-
error
|
|
331
|
-
{ combine : Form.Validation.Validation error parsed named constraints1
|
|
332
|
-
, view : subView
|
|
333
|
-
}
|
|
334
|
-
data
|
|
335
|
-
msg
|
|
336
|
-
)
|
|
337
|
-
->
|
|
338
|
-
Form
|
|
339
|
-
error
|
|
340
|
-
--((decider -> Validation error parsed named) -> combined)
|
|
341
|
-
({ combine : decider -> Form.Validation.Validation error parsed named constraints1
|
|
342
|
-
, view : decider -> subView
|
|
343
|
-
}
|
|
344
|
-
-> combineAndView
|
|
345
|
-
)
|
|
346
|
-
data
|
|
347
|
-
msg
|
|
348
|
-
->
|
|
349
|
-
Form
|
|
350
|
-
error
|
|
351
|
-
combineAndView
|
|
352
|
-
data
|
|
353
|
-
msg
|
|
354
|
-
dynamic forms formBuilder =
|
|
355
|
-
Form
|
|
356
|
-
{ submitStrategy = TransitionStrategy
|
|
357
|
-
, method = Post
|
|
358
|
-
, onSubmit = Nothing
|
|
359
|
-
}
|
|
360
|
-
[]
|
|
361
|
-
(\maybeData formState ->
|
|
362
|
-
let
|
|
363
|
-
toParser :
|
|
364
|
-
decider
|
|
365
|
-
->
|
|
366
|
-
{ result : Dict String (List error)
|
|
367
|
-
, isMatchCandidate : Bool
|
|
368
|
-
, combineAndView : { combine : Validation error parsed named constraints1, view : subView }
|
|
369
|
-
}
|
|
370
|
-
toParser decider =
|
|
371
|
-
case forms decider of
|
|
372
|
-
Form _ _ parseFn _ ->
|
|
373
|
-
-- TODO need to include hidden form fields from `definitions` (should they be automatically rendered? Does that mean the view type needs to be hardcoded?)
|
|
374
|
-
parseFn maybeData formState
|
|
375
|
-
|
|
376
|
-
myFn :
|
|
377
|
-
{ result : Dict String (List error)
|
|
378
|
-
, isMatchCandidate : Bool
|
|
379
|
-
, combineAndView : combineAndView
|
|
380
|
-
}
|
|
381
|
-
myFn =
|
|
382
|
-
let
|
|
383
|
-
newThing :
|
|
384
|
-
{ result : Dict String (List error)
|
|
385
|
-
, isMatchCandidate : Bool
|
|
386
|
-
, combineAndView : { combine : decider -> Validation error parsed named constraints1, view : decider -> subView } -> combineAndView
|
|
387
|
-
}
|
|
388
|
-
newThing =
|
|
389
|
-
case formBuilder of
|
|
390
|
-
Form _ _ parseFn _ ->
|
|
391
|
-
parseFn maybeData formState
|
|
392
|
-
|
|
393
|
-
arg : { combine : decider -> Validation error parsed named constraints1, view : decider -> subView }
|
|
394
|
-
arg =
|
|
395
|
-
{ combine =
|
|
396
|
-
toParser
|
|
397
|
-
>> .combineAndView
|
|
398
|
-
>> .combine
|
|
399
|
-
, view =
|
|
400
|
-
\decider ->
|
|
401
|
-
decider
|
|
402
|
-
|> toParser
|
|
403
|
-
|> .combineAndView
|
|
404
|
-
|> .view
|
|
405
|
-
}
|
|
406
|
-
in
|
|
407
|
-
{ result =
|
|
408
|
-
newThing.result
|
|
409
|
-
, combineAndView =
|
|
410
|
-
newThing.combineAndView arg
|
|
411
|
-
, isMatchCandidate = newThing.isMatchCandidate
|
|
412
|
-
}
|
|
413
|
-
in
|
|
414
|
-
myFn
|
|
415
|
-
)
|
|
416
|
-
(\_ -> [])
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
--{-| -}
|
|
421
|
-
--subGroup :
|
|
422
|
-
-- Form error ( Maybe parsed, Dict String (List error) ) data (Context error data -> subView)
|
|
423
|
-
-- ->
|
|
424
|
-
-- Form
|
|
425
|
-
-- error
|
|
426
|
-
-- ({ value : parsed } -> combined)
|
|
427
|
-
-- data
|
|
428
|
-
-- (Context error data -> (subView -> combinedView))
|
|
429
|
-
-- -> Form error combined data (Context error data -> combinedView)
|
|
430
|
-
--subGroup forms formBuilder =
|
|
431
|
-
-- Form []
|
|
432
|
-
-- (\maybeData formState ->
|
|
433
|
-
-- let
|
|
434
|
-
-- toParser : { result : ( Maybe ( Maybe parsed, Dict String (List error) ), Dict String (List error) ), view : Context error data -> subView }
|
|
435
|
-
-- toParser =
|
|
436
|
-
-- case forms of
|
|
437
|
-
-- Form definitions parseFn toInitialValues ->
|
|
438
|
-
-- -- TODO need to include hidden form fields from `definitions` (should they be automatically rendered? Does that mean the view type needs to be hardcoded?)
|
|
439
|
-
-- parseFn maybeData formState
|
|
440
|
-
--
|
|
441
|
-
-- myFn :
|
|
442
|
-
-- { result : ( Maybe combined, Dict String (List error) )
|
|
443
|
-
-- , view : Context error data -> combinedView
|
|
444
|
-
-- }
|
|
445
|
-
-- myFn =
|
|
446
|
-
-- let
|
|
447
|
-
-- deciderToParsed : ( Maybe parsed, Dict String (List error) )
|
|
448
|
-
-- deciderToParsed =
|
|
449
|
-
-- toParser |> mergeResults
|
|
450
|
-
--
|
|
451
|
-
-- newThing : { result : ( Maybe ({ value : parsed } -> combined), Dict String (List error) ), view : Context error data -> subView -> combinedView }
|
|
452
|
-
-- newThing =
|
|
453
|
-
-- case formBuilder of
|
|
454
|
-
-- Form definitions parseFn toInitialValues ->
|
|
455
|
-
-- parseFn maybeData formState
|
|
456
|
-
--
|
|
457
|
-
-- anotherThing : Maybe combined
|
|
458
|
-
-- anotherThing =
|
|
459
|
-
-- Maybe.map2
|
|
460
|
-
-- (\runFn parsed ->
|
|
461
|
-
-- runFn { value = parsed }
|
|
462
|
-
-- )
|
|
463
|
-
-- (Tuple.first newThing.result)
|
|
464
|
-
-- (deciderToParsed |> Tuple.first)
|
|
465
|
-
-- in
|
|
466
|
-
-- { result =
|
|
467
|
-
-- ( anotherThing
|
|
468
|
-
-- , mergeErrors (newThing.result |> Tuple.second)
|
|
469
|
-
-- (deciderToParsed |> Tuple.second)
|
|
470
|
-
-- )
|
|
471
|
-
-- , view =
|
|
472
|
-
-- \fieldErrors ->
|
|
473
|
-
-- let
|
|
474
|
-
-- something2 : subView
|
|
475
|
-
-- something2 =
|
|
476
|
-
-- fieldErrors
|
|
477
|
-
-- |> (toParser
|
|
478
|
-
-- |> .view
|
|
479
|
-
-- )
|
|
480
|
-
-- in
|
|
481
|
-
-- newThing.view fieldErrors something2
|
|
482
|
-
-- }
|
|
483
|
-
-- in
|
|
484
|
-
-- myFn
|
|
485
|
-
-- )
|
|
486
|
-
-- (\_ -> [])
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
{-| Declare a visible field for the form.
|
|
490
|
-
|
|
491
|
-
Use [`Form.Field`](Form-Field) to define the field and its validations.
|
|
492
|
-
|
|
493
|
-
form =
|
|
494
|
-
Form.init
|
|
495
|
-
(\email ->
|
|
496
|
-
{ combine =
|
|
497
|
-
Validation.succeed NewUser
|
|
498
|
-
|> Validation.andMap email
|
|
499
|
-
, view = \info -> [{- render fields -}]
|
|
500
|
-
}
|
|
501
|
-
)
|
|
502
|
-
|> Form.field "email"
|
|
503
|
-
(Field.text |> Field.required "Required")
|
|
504
|
-
|
|
505
|
-
-}
|
|
506
|
-
field :
|
|
507
|
-
String
|
|
508
|
-
-> Field error parsed data kind constraints
|
|
509
|
-
-> Form error (Form.Validation.Field error parsed kind -> combineAndView) data msg
|
|
510
|
-
-> Form error combineAndView data msg
|
|
511
|
-
field name (Field fieldParser kind) (Form renderOptions definitions parseFn toInitialValues) =
|
|
512
|
-
Form renderOptions
|
|
513
|
-
(( name, RegularField )
|
|
514
|
-
:: definitions
|
|
515
|
-
)
|
|
516
|
-
(\maybeData formState ->
|
|
517
|
-
let
|
|
518
|
-
( maybeParsed, errors ) =
|
|
519
|
-
-- @@@@@@ use code from here
|
|
520
|
-
fieldParser.decode rawFieldValue
|
|
521
|
-
|
|
522
|
-
( rawFieldValue, fieldStatus ) =
|
|
523
|
-
case formState.fields |> Dict.get name of
|
|
524
|
-
Just info ->
|
|
525
|
-
( Just info.value, info.status )
|
|
526
|
-
|
|
527
|
-
Nothing ->
|
|
528
|
-
( Maybe.map2 (|>) maybeData fieldParser.initialValue |> Maybe.andThen identity, FieldStatus.NotVisited )
|
|
529
|
-
|
|
530
|
-
thing : Pages.Internal.Form.ViewField kind
|
|
531
|
-
thing =
|
|
532
|
-
{ value = rawFieldValue
|
|
533
|
-
, status = fieldStatus
|
|
534
|
-
, kind = ( kind, fieldParser.properties )
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
parsedField : Form.Validation.Field error parsed kind
|
|
538
|
-
parsedField =
|
|
539
|
-
Pages.Internal.Form.Validation (Just thing) (Just name) ( maybeParsed, Dict.empty )
|
|
540
|
-
|
|
541
|
-
myFn :
|
|
542
|
-
{ result : Dict String (List error)
|
|
543
|
-
, combineAndView : Form.Validation.Field error parsed kind -> combineAndView
|
|
544
|
-
, isMatchCandidate : Bool
|
|
545
|
-
}
|
|
546
|
-
->
|
|
547
|
-
{ result : Dict String (List error)
|
|
548
|
-
, combineAndView : combineAndView
|
|
549
|
-
, isMatchCandidate : Bool
|
|
550
|
-
}
|
|
551
|
-
myFn soFar =
|
|
552
|
-
let
|
|
553
|
-
validationField : Form.Validation.Field error parsed kind
|
|
554
|
-
validationField =
|
|
555
|
-
parsedField
|
|
556
|
-
in
|
|
557
|
-
{ result =
|
|
558
|
-
soFar.result
|
|
559
|
-
|> addErrorsInternal name errors
|
|
560
|
-
, combineAndView =
|
|
561
|
-
soFar.combineAndView validationField
|
|
562
|
-
, isMatchCandidate = soFar.isMatchCandidate
|
|
563
|
-
}
|
|
564
|
-
in
|
|
565
|
-
formState
|
|
566
|
-
|> parseFn maybeData
|
|
567
|
-
|> myFn
|
|
568
|
-
)
|
|
569
|
-
(\data ->
|
|
570
|
-
case fieldParser.initialValue of
|
|
571
|
-
Just toInitialValue ->
|
|
572
|
-
( name, toInitialValue data )
|
|
573
|
-
:: toInitialValues data
|
|
574
|
-
|
|
575
|
-
Nothing ->
|
|
576
|
-
toInitialValues data
|
|
577
|
-
)
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
{-| Declare a hidden field for the form.
|
|
581
|
-
|
|
582
|
-
Unlike [`field`](#field) declarations which are rendered using [`Form.FieldView`](Form-FieldView)
|
|
583
|
-
functions, `hiddenField` inputs are automatically inserted into the form when you render it.
|
|
584
|
-
|
|
585
|
-
You define the field's validations the same way as for `field`, with the
|
|
586
|
-
[`Form.Field`](Form-Field) API.
|
|
587
|
-
|
|
588
|
-
form =
|
|
589
|
-
Form.init
|
|
590
|
-
(\quantity productId ->
|
|
591
|
-
{ combine = {- combine fields -}
|
|
592
|
-
, view = \info -> [{- render visible fields -}]
|
|
593
|
-
}
|
|
594
|
-
)
|
|
595
|
-
|> Form.field "quantity"
|
|
596
|
-
(Field.int |> Field.required "Required")
|
|
597
|
-
|> Form.field "productId"
|
|
598
|
-
(Field.text
|
|
599
|
-
|> Field.required "Required"
|
|
600
|
-
|> Field.withInitialValue (\product -> Form.Value.string product.id)
|
|
601
|
-
)
|
|
602
|
-
|
|
603
|
-
-}
|
|
604
|
-
hiddenField :
|
|
605
|
-
String
|
|
606
|
-
-> Field error parsed data kind constraints
|
|
607
|
-
-> Form error (Form.Validation.Field error parsed Form.FieldView.Hidden -> combineAndView) data msg
|
|
608
|
-
-> Form error combineAndView data msg
|
|
609
|
-
hiddenField name (Field fieldParser _) (Form options definitions parseFn toInitialValues) =
|
|
610
|
-
Form options
|
|
611
|
-
(( name, HiddenField )
|
|
612
|
-
:: definitions
|
|
613
|
-
)
|
|
614
|
-
(\maybeData formState ->
|
|
615
|
-
let
|
|
616
|
-
( maybeParsed, errors ) =
|
|
617
|
-
fieldParser.decode rawFieldValue
|
|
618
|
-
|
|
619
|
-
( rawFieldValue, fieldStatus ) =
|
|
620
|
-
case formState.fields |> Dict.get name of
|
|
621
|
-
Just info ->
|
|
622
|
-
( Just info.value, info.status )
|
|
623
|
-
|
|
624
|
-
Nothing ->
|
|
625
|
-
( Maybe.map2 (|>) maybeData fieldParser.initialValue |> Maybe.andThen identity, FieldStatus.NotVisited )
|
|
626
|
-
|
|
627
|
-
thing : Pages.Internal.Form.ViewField Form.FieldView.Hidden
|
|
628
|
-
thing =
|
|
629
|
-
{ value = rawFieldValue
|
|
630
|
-
, status = fieldStatus
|
|
631
|
-
, kind = ( Form.FieldView.Hidden, fieldParser.properties )
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
parsedField : Form.Validation.Field error parsed Form.FieldView.Hidden
|
|
635
|
-
parsedField =
|
|
636
|
-
Pages.Internal.Form.Validation (Just thing) (Just name) ( maybeParsed, Dict.empty )
|
|
637
|
-
|
|
638
|
-
myFn :
|
|
639
|
-
{ result : Dict String (List error)
|
|
640
|
-
, combineAndView : Form.Validation.Field error parsed Form.FieldView.Hidden -> combineAndView
|
|
641
|
-
, isMatchCandidate : Bool
|
|
642
|
-
}
|
|
643
|
-
->
|
|
644
|
-
{ result : Dict String (List error)
|
|
645
|
-
, combineAndView : combineAndView
|
|
646
|
-
, isMatchCandidate : Bool
|
|
647
|
-
}
|
|
648
|
-
myFn soFar =
|
|
649
|
-
let
|
|
650
|
-
validationField : Form.Validation.Field error parsed Form.FieldView.Hidden
|
|
651
|
-
validationField =
|
|
652
|
-
parsedField
|
|
653
|
-
in
|
|
654
|
-
{ result =
|
|
655
|
-
soFar.result
|
|
656
|
-
|> addErrorsInternal name errors
|
|
657
|
-
, combineAndView =
|
|
658
|
-
soFar.combineAndView validationField
|
|
659
|
-
, isMatchCandidate = soFar.isMatchCandidate
|
|
660
|
-
}
|
|
661
|
-
in
|
|
662
|
-
formState
|
|
663
|
-
|> parseFn maybeData
|
|
664
|
-
|> myFn
|
|
665
|
-
)
|
|
666
|
-
(\data ->
|
|
667
|
-
case fieldParser.initialValue of
|
|
668
|
-
Just toInitialValue ->
|
|
669
|
-
( name, toInitialValue data )
|
|
670
|
-
:: toInitialValues data
|
|
671
|
-
|
|
672
|
-
Nothing ->
|
|
673
|
-
toInitialValues data
|
|
674
|
-
)
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
{-| -}
|
|
678
|
-
toServerForm :
|
|
679
|
-
Form
|
|
680
|
-
error
|
|
681
|
-
{ combine : Form.Validation.Validation error combined kind constraints
|
|
682
|
-
, view : viewFn
|
|
683
|
-
}
|
|
684
|
-
data
|
|
685
|
-
msg
|
|
686
|
-
->
|
|
687
|
-
Form
|
|
688
|
-
error
|
|
689
|
-
{ combine : Form.Validation.Validation error (BackendTask FatalError (Form.Validation.Validation error combined kind constraints)) kind constraints
|
|
690
|
-
, view : viewFn
|
|
691
|
-
}
|
|
692
|
-
data
|
|
693
|
-
msg
|
|
694
|
-
toServerForm (Form options a b c) =
|
|
695
|
-
let
|
|
696
|
-
mappedB :
|
|
697
|
-
Maybe data
|
|
698
|
-
-> FormState
|
|
699
|
-
->
|
|
700
|
-
{ result : Dict String (List error)
|
|
701
|
-
, isMatchCandidate : Bool
|
|
702
|
-
, combineAndView :
|
|
703
|
-
{ combine : Form.Validation.Validation error (BackendTask FatalError (Form.Validation.Validation error combined kind constraints)) kind constraints
|
|
704
|
-
, view : viewFn
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
mappedB maybeData formState =
|
|
708
|
-
b maybeData formState
|
|
709
|
-
|> (\thing ->
|
|
710
|
-
{ result = thing.result
|
|
711
|
-
, combineAndView =
|
|
712
|
-
{ combine =
|
|
713
|
-
thing.combineAndView.combine
|
|
714
|
-
|> BackendTask.succeed
|
|
715
|
-
|> Form.Validation.succeed2
|
|
716
|
-
, view = thing.combineAndView.view
|
|
717
|
-
}
|
|
718
|
-
, isMatchCandidate = thing.isMatchCandidate
|
|
719
|
-
}
|
|
720
|
-
)
|
|
721
|
-
in
|
|
722
|
-
Form options a mappedB c
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
{-| -}
|
|
726
|
-
hiddenKind :
|
|
727
|
-
( String, String )
|
|
728
|
-
-> error
|
|
729
|
-
-> Form error combineAndView data msg
|
|
730
|
-
-> Form error combineAndView data msg
|
|
731
|
-
hiddenKind ( name, value ) error_ (Form options definitions parseFn toInitialValues) =
|
|
732
|
-
let
|
|
733
|
-
(Field fieldParser _) =
|
|
734
|
-
Field.exactValue value error_
|
|
735
|
-
in
|
|
736
|
-
Form options
|
|
737
|
-
(( name, HiddenField )
|
|
738
|
-
:: definitions
|
|
739
|
-
)
|
|
740
|
-
(\maybeData formState ->
|
|
741
|
-
let
|
|
742
|
-
( decodedValue, errors ) =
|
|
743
|
-
fieldParser.decode rawFieldValue
|
|
744
|
-
|
|
745
|
-
rawFieldValue : Maybe String
|
|
746
|
-
rawFieldValue =
|
|
747
|
-
case formState.fields |> Dict.get name of
|
|
748
|
-
Just info ->
|
|
749
|
-
Just info.value
|
|
750
|
-
|
|
751
|
-
Nothing ->
|
|
752
|
-
Maybe.map2 (|>) maybeData fieldParser.initialValue
|
|
753
|
-
|> Maybe.andThen identity
|
|
754
|
-
|
|
755
|
-
myFn :
|
|
756
|
-
{ result : Dict String (List error)
|
|
757
|
-
, isMatchCandidate : Bool
|
|
758
|
-
, combineAndView : combineAndView
|
|
759
|
-
}
|
|
760
|
-
->
|
|
761
|
-
{ result : Dict String (List error)
|
|
762
|
-
, isMatchCandidate : Bool
|
|
763
|
-
, combineAndView : combineAndView
|
|
764
|
-
}
|
|
765
|
-
myFn soFar =
|
|
766
|
-
{ result =
|
|
767
|
-
soFar.result
|
|
768
|
-
|> addErrorsInternal name errors
|
|
769
|
-
, combineAndView = soFar.combineAndView
|
|
770
|
-
, isMatchCandidate = soFar.isMatchCandidate && decodedValue == Just value
|
|
771
|
-
}
|
|
772
|
-
in
|
|
773
|
-
formState
|
|
774
|
-
|> parseFn maybeData
|
|
775
|
-
|> myFn
|
|
776
|
-
)
|
|
777
|
-
(\data ->
|
|
778
|
-
case fieldParser.initialValue of
|
|
779
|
-
Just toInitialValue ->
|
|
780
|
-
( name, toInitialValue data )
|
|
781
|
-
:: toInitialValues data
|
|
782
|
-
|
|
783
|
-
Nothing ->
|
|
784
|
-
toInitialValues data
|
|
785
|
-
)
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
{-| -}
|
|
789
|
-
type Errors error
|
|
790
|
-
= Errors (Dict String (List error))
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
{-| -}
|
|
794
|
-
errorsForField : Form.Validation.Field error parsed kind -> Errors error -> List error
|
|
795
|
-
errorsForField field_ (Errors errorsDict) =
|
|
796
|
-
errorsDict
|
|
797
|
-
|> Dict.get (Form.Validation.fieldName field_)
|
|
798
|
-
|> Maybe.withDefault []
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
{-| -}
|
|
802
|
-
type alias AppContext app actionData =
|
|
803
|
-
{ app
|
|
804
|
-
| --, sharedData : Shared.Data
|
|
805
|
-
--, routeParams : routeParams
|
|
806
|
-
path : Path
|
|
807
|
-
, action : Maybe actionData
|
|
808
|
-
|
|
809
|
-
--, submit :
|
|
810
|
-
-- { fields : List ( String, String ), headers : List ( String, String ) }
|
|
811
|
-
-- -> Pages.Fetcher.Fetcher (Result Http.Error action)
|
|
812
|
-
, transition : Maybe Transition
|
|
813
|
-
, fetchers : Dict String (Pages.Transition.FetcherState (Maybe actionData))
|
|
814
|
-
, pageFormState :
|
|
815
|
-
Dict String { fields : Dict String { value : String, status : FieldStatus }, submitAttempted : Bool }
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
mergeResults :
|
|
820
|
-
{ a | result : ( Validation error parsed named constraints1, Dict String (List error) ) }
|
|
821
|
-
-> Validation error parsed unnamed constraints2
|
|
822
|
-
mergeResults parsed =
|
|
823
|
-
case parsed.result of
|
|
824
|
-
( Pages.Internal.Form.Validation _ name ( parsedThing, combineErrors ), individualFieldErrors ) ->
|
|
825
|
-
Pages.Internal.Form.Validation Nothing
|
|
826
|
-
name
|
|
827
|
-
( parsedThing
|
|
828
|
-
, mergeErrors combineErrors individualFieldErrors
|
|
829
|
-
)
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
mergeErrors : Dict comparable (List value) -> Dict comparable (List value) -> Dict comparable (List value)
|
|
833
|
-
mergeErrors errors1 errors2 =
|
|
834
|
-
Dict.merge
|
|
835
|
-
(\key entries soFar ->
|
|
836
|
-
soFar |> insertIfNonempty key entries
|
|
837
|
-
)
|
|
838
|
-
(\key entries1 entries2 soFar ->
|
|
839
|
-
soFar |> insertIfNonempty key (entries1 ++ entries2)
|
|
840
|
-
)
|
|
841
|
-
(\key entries soFar ->
|
|
842
|
-
soFar |> insertIfNonempty key entries
|
|
843
|
-
)
|
|
844
|
-
errors1
|
|
845
|
-
errors2
|
|
846
|
-
Dict.empty
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
{-| -}
|
|
850
|
-
parse :
|
|
851
|
-
String
|
|
852
|
-
-> AppContext app actionData
|
|
853
|
-
-> data
|
|
854
|
-
-> Form error { info | combine : Form.Validation.Validation error parsed named constraints } data msg
|
|
855
|
-
-> ( Maybe parsed, Dict String (List error) )
|
|
856
|
-
parse formId app data (Form _ _ parser _) =
|
|
857
|
-
-- TODO Get transition context from `app` so you can check if the current form is being submitted
|
|
858
|
-
-- TODO either as a transition or a fetcher? Should be easy enough to check for the `id` on either of those?
|
|
859
|
-
let
|
|
860
|
-
parsed :
|
|
861
|
-
{ result : Dict String (List error)
|
|
862
|
-
, isMatchCandidate : Bool
|
|
863
|
-
, combineAndView : { info | combine : Validation error parsed named constraints }
|
|
864
|
-
}
|
|
865
|
-
parsed =
|
|
866
|
-
parser (Just data) thisFormState
|
|
867
|
-
|
|
868
|
-
thisFormState : FormState
|
|
869
|
-
thisFormState =
|
|
870
|
-
app.pageFormState
|
|
871
|
-
|> Dict.get formId
|
|
872
|
-
|> Maybe.withDefault initFormState
|
|
873
|
-
in
|
|
874
|
-
{ result = ( parsed.combineAndView.combine, parsed.result )
|
|
875
|
-
}
|
|
876
|
-
|> mergeResults
|
|
877
|
-
|> unwrapValidation
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
insertIfNonempty : comparable -> List value -> Dict comparable (List value) -> Dict comparable (List value)
|
|
881
|
-
insertIfNonempty key values dict =
|
|
882
|
-
if values |> List.isEmpty then
|
|
883
|
-
dict
|
|
884
|
-
|
|
885
|
-
else
|
|
886
|
-
dict
|
|
887
|
-
|> Dict.insert key values
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
{-| -}
|
|
891
|
-
runServerSide :
|
|
892
|
-
List ( String, String )
|
|
893
|
-
-> Form error (Form.Validation.Validation error parsed kind constraints) data msg
|
|
894
|
-
-> ( Bool, ( Maybe parsed, Dict String (List error) ) )
|
|
895
|
-
runServerSide rawFormData (Form _ _ parser _) =
|
|
896
|
-
let
|
|
897
|
-
parsed :
|
|
898
|
-
{ result : Dict String (List error)
|
|
899
|
-
, isMatchCandidate : Bool
|
|
900
|
-
, combineAndView : Validation error parsed kind constraints
|
|
901
|
-
}
|
|
902
|
-
parsed =
|
|
903
|
-
parser Nothing thisFormState
|
|
904
|
-
|
|
905
|
-
thisFormState : FormState
|
|
906
|
-
thisFormState =
|
|
907
|
-
{ initFormState
|
|
908
|
-
| fields =
|
|
909
|
-
rawFormData
|
|
910
|
-
|> List.map
|
|
911
|
-
(Tuple.mapSecond
|
|
912
|
-
(\value ->
|
|
913
|
-
{ value = value
|
|
914
|
-
, status = FieldStatus.NotVisited
|
|
915
|
-
}
|
|
916
|
-
)
|
|
917
|
-
)
|
|
918
|
-
|> Dict.fromList
|
|
919
|
-
}
|
|
920
|
-
in
|
|
921
|
-
( parsed.isMatchCandidate
|
|
922
|
-
, { result = ( parsed.combineAndView, parsed.result )
|
|
923
|
-
}
|
|
924
|
-
|> mergeResults
|
|
925
|
-
|> unwrapValidation
|
|
926
|
-
)
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
unwrapValidation : Validation error parsed named constraints -> ( Maybe parsed, Dict String (List error) )
|
|
930
|
-
unwrapValidation (Pages.Internal.Form.Validation _ _ ( maybeParsed, errors )) =
|
|
931
|
-
( maybeParsed, errors )
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
{-| -}
|
|
935
|
-
runOneOfServerSide :
|
|
936
|
-
List ( String, String )
|
|
937
|
-
-> ServerForms error parsed
|
|
938
|
-
-> ( Maybe parsed, Dict String (List error) )
|
|
939
|
-
runOneOfServerSide rawFormData forms =
|
|
940
|
-
runOneOfServerSideHelp rawFormData Nothing forms
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
{-| -}
|
|
944
|
-
runOneOfServerSideHelp :
|
|
945
|
-
List ( String, String )
|
|
946
|
-
-> Maybe (List ( String, List error ))
|
|
947
|
-
-> ServerForms error parsed
|
|
948
|
-
-> ( Maybe parsed, Dict String (List error) )
|
|
949
|
-
runOneOfServerSideHelp rawFormData firstFoundErrors (ServerForms parsers) =
|
|
950
|
-
case parsers of
|
|
951
|
-
firstParser :: remainingParsers ->
|
|
952
|
-
let
|
|
953
|
-
( isMatchCandidate, thing1 ) =
|
|
954
|
-
runServerSide rawFormData firstParser
|
|
955
|
-
|
|
956
|
-
thing : ( Maybe parsed, List ( String, List error ) )
|
|
957
|
-
thing =
|
|
958
|
-
thing1
|
|
959
|
-
|> Tuple.mapSecond
|
|
960
|
-
(\errors ->
|
|
961
|
-
errors
|
|
962
|
-
|> Dict.toList
|
|
963
|
-
|> List.filter (Tuple.second >> List.isEmpty >> not)
|
|
964
|
-
)
|
|
965
|
-
in
|
|
966
|
-
case ( isMatchCandidate, thing ) of
|
|
967
|
-
( True, ( Just parsed, errors ) ) ->
|
|
968
|
-
( Just parsed, errors |> Dict.fromList )
|
|
969
|
-
|
|
970
|
-
( _, ( _, errors ) ) ->
|
|
971
|
-
runOneOfServerSideHelp rawFormData
|
|
972
|
-
(firstFoundErrors
|
|
973
|
-
-- TODO is this logic what we want here? Might need to think through the semantics a bit more
|
|
974
|
-
-- of which errors to parse into - could be the first errors, the last, or some other way of
|
|
975
|
-
-- having higher precedence for deciding which form should be used
|
|
976
|
-
|> Maybe.withDefault errors
|
|
977
|
-
|> Just
|
|
978
|
-
)
|
|
979
|
-
(ServerForms remainingParsers)
|
|
980
|
-
|
|
981
|
-
[] ->
|
|
982
|
-
-- TODO need to pass errors
|
|
983
|
-
( Nothing, firstFoundErrors |> Maybe.withDefault [] |> Dict.fromList )
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
{-| -}
|
|
987
|
-
renderHtml :
|
|
988
|
-
String
|
|
989
|
-
-> List (Html.Attribute (PagesMsg msg))
|
|
990
|
-
-> (actionData -> Maybe (Response error))
|
|
991
|
-
-> AppContext app actionData
|
|
992
|
-
-> input
|
|
993
|
-
->
|
|
994
|
-
Form
|
|
995
|
-
error
|
|
996
|
-
{ combine : Form.Validation.Validation error parsed named constraints
|
|
997
|
-
, view : Context error input -> List (Html (PagesMsg msg))
|
|
998
|
-
}
|
|
999
|
-
input
|
|
1000
|
-
msg
|
|
1001
|
-
-> Html (PagesMsg msg)
|
|
1002
|
-
renderHtml formId attrs accessResponse app data form =
|
|
1003
|
-
Html.Lazy.lazy6 renderHelper formId attrs accessResponse app data form
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
{-| -}
|
|
1007
|
-
toDynamicFetcher :
|
|
1008
|
-
Form
|
|
1009
|
-
error
|
|
1010
|
-
{ combine : Form.Validation.Validation error parsed field constraints
|
|
1011
|
-
, view : Context error data -> view
|
|
1012
|
-
}
|
|
1013
|
-
data
|
|
1014
|
-
userMsg
|
|
1015
|
-
->
|
|
1016
|
-
Form
|
|
1017
|
-
error
|
|
1018
|
-
{ combine : Form.Validation.Validation error parsed field constraints
|
|
1019
|
-
, view : Context error data -> view
|
|
1020
|
-
}
|
|
1021
|
-
data
|
|
1022
|
-
userMsg
|
|
1023
|
-
toDynamicFetcher (Form renderOptions a b c) =
|
|
1024
|
-
Form { renderOptions | submitStrategy = FetcherStrategy } a b c
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
{-| -}
|
|
1028
|
-
withGetMethod : Form error combineAndView input userMsg -> Form error combineAndView input userMsg
|
|
1029
|
-
withGetMethod (Form options a b c) =
|
|
1030
|
-
Form { options | method = Get } a b c
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
{-| -}
|
|
1034
|
-
withOnSubmit : ({ fields : List ( String, String ) } -> userMsg) -> Form error combineAndView input oldMsg -> Form error combineAndView input userMsg
|
|
1035
|
-
withOnSubmit onSubmit (Form options a b c) =
|
|
1036
|
-
Form
|
|
1037
|
-
{ onSubmit = Just onSubmit
|
|
1038
|
-
, submitStrategy = options.submitStrategy
|
|
1039
|
-
, method = options.method
|
|
1040
|
-
}
|
|
1041
|
-
a
|
|
1042
|
-
b
|
|
1043
|
-
c
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
{-| -}
|
|
1047
|
-
renderStyledHtml :
|
|
1048
|
-
String
|
|
1049
|
-
-> List (Html.Styled.Attribute (PagesMsg msg))
|
|
1050
|
-
-> (actionData -> Maybe (Response error))
|
|
1051
|
-
-> AppContext app actionData
|
|
1052
|
-
-> input
|
|
1053
|
-
->
|
|
1054
|
-
Form
|
|
1055
|
-
error
|
|
1056
|
-
{ combine : Form.Validation.Validation error parsed field constraints
|
|
1057
|
-
, view : Context error input -> List (Html.Styled.Html (PagesMsg msg))
|
|
1058
|
-
}
|
|
1059
|
-
input
|
|
1060
|
-
msg
|
|
1061
|
-
-> Html.Styled.Html (PagesMsg msg)
|
|
1062
|
-
renderStyledHtml formId attrs accessResponse app data form =
|
|
1063
|
-
Html.Styled.Lazy.lazy6 renderStyledHelper formId attrs accessResponse app data form
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
{-| -}
|
|
1067
|
-
type alias Response error =
|
|
1068
|
-
Pages.Internal.Form.Response error
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
renderHelper :
|
|
1072
|
-
String
|
|
1073
|
-
-> List (Html.Attribute (PagesMsg msg))
|
|
1074
|
-
-> (actionData -> Maybe (Response error))
|
|
1075
|
-
-> AppContext app actionData
|
|
1076
|
-
-> data
|
|
1077
|
-
->
|
|
1078
|
-
Form
|
|
1079
|
-
error
|
|
1080
|
-
{ combine : Form.Validation.Validation error parsed named constraints
|
|
1081
|
-
, view : Context error data -> List (Html (PagesMsg msg))
|
|
1082
|
-
}
|
|
1083
|
-
data
|
|
1084
|
-
msg
|
|
1085
|
-
-> Html (PagesMsg msg)
|
|
1086
|
-
renderHelper formId attrs accessResponse formState data ((Form options _ _ _) as form) =
|
|
1087
|
-
-- TODO Get transition context from `app` so you can check if the current form is being submitted
|
|
1088
|
-
-- TODO either as a transition or a fetcher? Should be easy enough to check for the `id` on either of those?
|
|
1089
|
-
let
|
|
1090
|
-
{ hiddenInputs, children, isValid } =
|
|
1091
|
-
helperValues formId toHiddenInput accessResponse formState data form
|
|
1092
|
-
|
|
1093
|
-
toHiddenInput : List (Html.Attribute (PagesMsg msg)) -> Html (PagesMsg msg)
|
|
1094
|
-
toHiddenInput hiddenAttrs =
|
|
1095
|
-
Html.input hiddenAttrs []
|
|
1096
|
-
in
|
|
1097
|
-
Html.form
|
|
1098
|
-
(Form.listeners formId
|
|
1099
|
-
++ [ Attr.method (methodToString options.method)
|
|
1100
|
-
, Attr.novalidate True
|
|
1101
|
-
|
|
1102
|
-
-- TODO provide a way to override the action so users can submit to other Routes
|
|
1103
|
-
, Attr.action (Path.toAbsolute formState.path)
|
|
1104
|
-
, case options.submitStrategy of
|
|
1105
|
-
FetcherStrategy ->
|
|
1106
|
-
Pages.Internal.Msg.fetcherOnSubmit options.onSubmit formId (\_ -> isValid)
|
|
1107
|
-
|
|
1108
|
-
TransitionStrategy ->
|
|
1109
|
-
Pages.Internal.Msg.submitIfValid options.onSubmit formId (\_ -> isValid)
|
|
1110
|
-
]
|
|
1111
|
-
++ attrs
|
|
1112
|
-
)
|
|
1113
|
-
(hiddenInputs ++ children)
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
renderStyledHelper :
|
|
1117
|
-
String
|
|
1118
|
-
-> List (Html.Styled.Attribute (PagesMsg msg))
|
|
1119
|
-
-> (actionData -> Maybe (Response error))
|
|
1120
|
-
-> AppContext app actionData
|
|
1121
|
-
-> data
|
|
1122
|
-
->
|
|
1123
|
-
Form
|
|
1124
|
-
error
|
|
1125
|
-
{ combine : Form.Validation.Validation error parsed field constraints
|
|
1126
|
-
, view : Context error data -> List (Html.Styled.Html (PagesMsg msg))
|
|
1127
|
-
}
|
|
1128
|
-
data
|
|
1129
|
-
msg
|
|
1130
|
-
-> Html.Styled.Html (PagesMsg msg)
|
|
1131
|
-
renderStyledHelper formId attrs accessResponse formState data ((Form options _ _ _) as form) =
|
|
1132
|
-
-- TODO Get transition context from `app` so you can check if the current form is being submitted
|
|
1133
|
-
-- TODO either as a transition or a fetcher? Should be easy enough to check for the `id` on either of those?
|
|
1134
|
-
let
|
|
1135
|
-
{ hiddenInputs, children, isValid } =
|
|
1136
|
-
helperValues formId toHiddenInput accessResponse formState data form
|
|
1137
|
-
|
|
1138
|
-
toHiddenInput : List (Html.Attribute (PagesMsg msg)) -> Html.Styled.Html (PagesMsg msg)
|
|
1139
|
-
toHiddenInput hiddenAttrs =
|
|
1140
|
-
Html.Styled.input (hiddenAttrs |> List.map StyledAttr.fromUnstyled) []
|
|
1141
|
-
in
|
|
1142
|
-
Html.Styled.form
|
|
1143
|
-
((Form.listeners formId |> List.map StyledAttr.fromUnstyled)
|
|
1144
|
-
++ [ StyledAttr.method (methodToString options.method)
|
|
1145
|
-
, StyledAttr.novalidate True
|
|
1146
|
-
, StyledAttr.action (Path.toAbsolute formState.path)
|
|
1147
|
-
, case options.submitStrategy of
|
|
1148
|
-
FetcherStrategy ->
|
|
1149
|
-
StyledAttr.fromUnstyled <|
|
|
1150
|
-
Pages.Internal.Msg.fetcherOnSubmit options.onSubmit formId (\_ -> isValid)
|
|
1151
|
-
|
|
1152
|
-
TransitionStrategy ->
|
|
1153
|
-
StyledAttr.fromUnstyled <|
|
|
1154
|
-
Pages.Internal.Msg.submitIfValid options.onSubmit formId (\_ -> isValid)
|
|
1155
|
-
]
|
|
1156
|
-
++ attrs
|
|
1157
|
-
)
|
|
1158
|
-
(hiddenInputs ++ children)
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
helperValues :
|
|
1162
|
-
String
|
|
1163
|
-
-> (List (Html.Attribute (PagesMsg msg)) -> view)
|
|
1164
|
-
-> (actionData -> Maybe (Response error))
|
|
1165
|
-
-> AppContext app actionData
|
|
1166
|
-
-> data
|
|
1167
|
-
->
|
|
1168
|
-
Form
|
|
1169
|
-
error
|
|
1170
|
-
{ combine : Form.Validation.Validation error parsed field constraints
|
|
1171
|
-
, view : Context error data -> List view
|
|
1172
|
-
}
|
|
1173
|
-
data
|
|
1174
|
-
msg
|
|
1175
|
-
-> { hiddenInputs : List view, children : List view, isValid : Bool }
|
|
1176
|
-
helperValues formId toHiddenInput accessResponse formState data (Form _ fieldDefinitions parser toInitialValues) =
|
|
1177
|
-
let
|
|
1178
|
-
initialValues : Dict String Form.FieldState
|
|
1179
|
-
initialValues =
|
|
1180
|
-
toInitialValues data
|
|
1181
|
-
|> List.filterMap
|
|
1182
|
-
(\( key, maybeValue ) ->
|
|
1183
|
-
maybeValue
|
|
1184
|
-
|> Maybe.map
|
|
1185
|
-
(\value ->
|
|
1186
|
-
( key, { value = value, status = FieldStatus.NotVisited } )
|
|
1187
|
-
)
|
|
1188
|
-
)
|
|
1189
|
-
|> Dict.fromList
|
|
1190
|
-
|
|
1191
|
-
part2 : Dict String Form.FieldState
|
|
1192
|
-
part2 =
|
|
1193
|
-
formState.pageFormState
|
|
1194
|
-
|> Dict.get formId
|
|
1195
|
-
|> Maybe.withDefault
|
|
1196
|
-
(formState.action
|
|
1197
|
-
|> Maybe.andThen (accessResponse >> Maybe.map unwrapResponse)
|
|
1198
|
-
|> Maybe.map
|
|
1199
|
-
(\{ fields } ->
|
|
1200
|
-
{ fields =
|
|
1201
|
-
fields
|
|
1202
|
-
|> List.map (Tuple.mapSecond (\value -> { value = value, status = FieldStatus.NotVisited }))
|
|
1203
|
-
|> Dict.fromList
|
|
1204
|
-
, submitAttempted = True
|
|
1205
|
-
}
|
|
1206
|
-
)
|
|
1207
|
-
|> Maybe.withDefault initFormState
|
|
1208
|
-
)
|
|
1209
|
-
|> .fields
|
|
1210
|
-
|
|
1211
|
-
fullFormState : Dict String Form.FieldState
|
|
1212
|
-
fullFormState =
|
|
1213
|
-
initialValues
|
|
1214
|
-
|> Dict.union part2
|
|
1215
|
-
|
|
1216
|
-
parsed :
|
|
1217
|
-
{ result : ( Form.Validation.Validation error parsed field constraints, Dict String (List error) )
|
|
1218
|
-
, isMatchCandidate : Bool
|
|
1219
|
-
, view : Context error data -> List view
|
|
1220
|
-
}
|
|
1221
|
-
parsed =
|
|
1222
|
-
{ isMatchCandidate = parsed1.isMatchCandidate
|
|
1223
|
-
, view = parsed1.combineAndView.view
|
|
1224
|
-
, result = ( parsed1.combineAndView.combine, parsed1.result )
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
parsed1 :
|
|
1228
|
-
{ result : Dict String (List error)
|
|
1229
|
-
, isMatchCandidate : Bool
|
|
1230
|
-
, combineAndView : { combine : Form.Validation.Validation error parsed field constraints, view : Context error data -> List view }
|
|
1231
|
-
}
|
|
1232
|
-
parsed1 =
|
|
1233
|
-
parser (Just data) thisFormState
|
|
1234
|
-
|
|
1235
|
-
withoutServerErrors : Form.Validation.Validation error parsed named constraints
|
|
1236
|
-
withoutServerErrors =
|
|
1237
|
-
parsed |> mergeResults
|
|
1238
|
-
|
|
1239
|
-
withServerErrors : Form.Validation.Validation error parsed named constraints
|
|
1240
|
-
withServerErrors =
|
|
1241
|
-
mergeResults
|
|
1242
|
-
{ parsed
|
|
1243
|
-
| result =
|
|
1244
|
-
parsed.result
|
|
1245
|
-
|> Tuple.mapSecond
|
|
1246
|
-
(\errors1 ->
|
|
1247
|
-
mergeErrors errors1
|
|
1248
|
-
(formState.action
|
|
1249
|
-
|> Maybe.andThen (accessResponse >> Maybe.map (unwrapResponse >> .errors))
|
|
1250
|
-
|> Maybe.withDefault Dict.empty
|
|
1251
|
-
)
|
|
1252
|
-
)
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
thisFormState : FormState
|
|
1256
|
-
thisFormState =
|
|
1257
|
-
formState.pageFormState
|
|
1258
|
-
|> Dict.get formId
|
|
1259
|
-
|> Maybe.withDefault
|
|
1260
|
-
(formState.action
|
|
1261
|
-
|> Maybe.andThen (accessResponse >> Maybe.map unwrapResponse)
|
|
1262
|
-
|> Maybe.map
|
|
1263
|
-
(\{ fields } ->
|
|
1264
|
-
{ fields =
|
|
1265
|
-
fields
|
|
1266
|
-
|> List.map (Tuple.mapSecond (\value -> { value = value, status = FieldStatus.NotVisited }))
|
|
1267
|
-
|> Dict.fromList
|
|
1268
|
-
, submitAttempted = True
|
|
1269
|
-
}
|
|
1270
|
-
)
|
|
1271
|
-
|> Maybe.withDefault Form.init
|
|
1272
|
-
)
|
|
1273
|
-
|> (\state -> { state | fields = fullFormState })
|
|
1274
|
-
|
|
1275
|
-
context : Context error data
|
|
1276
|
-
context =
|
|
1277
|
-
{ errors =
|
|
1278
|
-
withServerErrors
|
|
1279
|
-
|> unwrapValidation
|
|
1280
|
-
|> Tuple.second
|
|
1281
|
-
|> Errors
|
|
1282
|
-
, isTransitioning =
|
|
1283
|
-
-- TODO instead of isTransitioning : Bool, it would be useful to get a custom type with the exact state
|
|
1284
|
-
(case formState.fetchers |> Dict.get formId of
|
|
1285
|
-
Just { status } ->
|
|
1286
|
-
case status of
|
|
1287
|
-
Pages.Transition.FetcherComplete _ ->
|
|
1288
|
-
False
|
|
1289
|
-
|
|
1290
|
-
Pages.Transition.FetcherSubmitting ->
|
|
1291
|
-
True
|
|
1292
|
-
|
|
1293
|
-
Pages.Transition.FetcherReloading _ ->
|
|
1294
|
-
True
|
|
1295
|
-
|
|
1296
|
-
Nothing ->
|
|
1297
|
-
False
|
|
1298
|
-
)
|
|
1299
|
-
|| (case formState.transition of
|
|
1300
|
-
Just (Submitting formData) ->
|
|
1301
|
-
formData.id == Just formId
|
|
1302
|
-
|
|
1303
|
-
Just (LoadAfterSubmit submitData _ _) ->
|
|
1304
|
-
submitData.id == Just formId
|
|
1305
|
-
|
|
1306
|
-
Just (Loading _ _) ->
|
|
1307
|
-
False
|
|
1308
|
-
|
|
1309
|
-
Nothing ->
|
|
1310
|
-
False
|
|
1311
|
-
)
|
|
1312
|
-
, submitAttempted = thisFormState.submitAttempted
|
|
1313
|
-
, data = data
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
children : List view
|
|
1317
|
-
children =
|
|
1318
|
-
parsed.view context
|
|
1319
|
-
|
|
1320
|
-
hiddenInputs : List view
|
|
1321
|
-
hiddenInputs =
|
|
1322
|
-
fieldDefinitions
|
|
1323
|
-
|> List.filterMap
|
|
1324
|
-
(\( name, fieldDefinition ) ->
|
|
1325
|
-
case fieldDefinition of
|
|
1326
|
-
HiddenField ->
|
|
1327
|
-
[ Attr.name name
|
|
1328
|
-
, Attr.type_ "hidden"
|
|
1329
|
-
, Attr.value
|
|
1330
|
-
(initialValues
|
|
1331
|
-
|> Dict.get name
|
|
1332
|
-
|> Maybe.map .value
|
|
1333
|
-
|> Maybe.withDefault ""
|
|
1334
|
-
)
|
|
1335
|
-
]
|
|
1336
|
-
|> toHiddenInput
|
|
1337
|
-
|> Just
|
|
1338
|
-
|
|
1339
|
-
RegularField ->
|
|
1340
|
-
Nothing
|
|
1341
|
-
)
|
|
1342
|
-
|
|
1343
|
-
isValid : Bool
|
|
1344
|
-
isValid =
|
|
1345
|
-
case withoutServerErrors of
|
|
1346
|
-
Validation _ _ ( Just _, errors ) ->
|
|
1347
|
-
Dict.isEmpty errors
|
|
1348
|
-
|
|
1349
|
-
_ ->
|
|
1350
|
-
False
|
|
1351
|
-
in
|
|
1352
|
-
{ hiddenInputs = hiddenInputs
|
|
1353
|
-
, children = children
|
|
1354
|
-
, isValid = isValid
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
{-| -}
|
|
1359
|
-
type alias DoneForm error parsed data view msg =
|
|
1360
|
-
Form
|
|
1361
|
-
error
|
|
1362
|
-
{ combine : Combined error parsed
|
|
1363
|
-
, view : Context error data -> view
|
|
1364
|
-
}
|
|
1365
|
-
data
|
|
1366
|
-
msg
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
{-| -}
|
|
1370
|
-
type alias HtmlForm error parsed input msg =
|
|
1371
|
-
Form
|
|
1372
|
-
error
|
|
1373
|
-
{ combine : Combined error parsed
|
|
1374
|
-
, view : Context error input -> List (Html (PagesMsg msg))
|
|
1375
|
-
}
|
|
1376
|
-
input
|
|
1377
|
-
msg
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
{-| -}
|
|
1381
|
-
type ServerForms error parsed
|
|
1382
|
-
= ServerForms
|
|
1383
|
-
(List
|
|
1384
|
-
(Form
|
|
1385
|
-
error
|
|
1386
|
-
(Combined error parsed)
|
|
1387
|
-
Never
|
|
1388
|
-
Never
|
|
1389
|
-
)
|
|
1390
|
-
)
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
{-| -}
|
|
1394
|
-
initCombined :
|
|
1395
|
-
(parsed -> combined)
|
|
1396
|
-
->
|
|
1397
|
-
Form
|
|
1398
|
-
error
|
|
1399
|
-
{ combineAndView
|
|
1400
|
-
| combine : Form.Validation.Validation error parsed kind constraints
|
|
1401
|
-
}
|
|
1402
|
-
input
|
|
1403
|
-
msg
|
|
1404
|
-
-> ServerForms error combined
|
|
1405
|
-
initCombined mapFn form =
|
|
1406
|
-
ServerForms [ normalizeServerForm mapFn form ]
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
normalizeServerForm :
|
|
1410
|
-
(parsed -> combined)
|
|
1411
|
-
-> Form error { combineAndView | combine : Form.Validation.Validation error parsed kind constraints } input msg
|
|
1412
|
-
-> Form error (Combined error combined) Never Never
|
|
1413
|
-
normalizeServerForm mapFn (Form options _ parseFn _) =
|
|
1414
|
-
Form
|
|
1415
|
-
{ onSubmit = Nothing
|
|
1416
|
-
, submitStrategy = options.submitStrategy
|
|
1417
|
-
, method = options.method
|
|
1418
|
-
}
|
|
1419
|
-
[]
|
|
1420
|
-
(\_ formState ->
|
|
1421
|
-
let
|
|
1422
|
-
parsed :
|
|
1423
|
-
{ result : Dict String (List error)
|
|
1424
|
-
, isMatchCandidate : Bool
|
|
1425
|
-
, combineAndView : { combineAndView | combine : Form.Validation.Validation error parsed kind constraints }
|
|
1426
|
-
}
|
|
1427
|
-
parsed =
|
|
1428
|
-
parseFn Nothing formState
|
|
1429
|
-
in
|
|
1430
|
-
{ result = parsed.result
|
|
1431
|
-
, combineAndView = parsed.combineAndView.combine |> Form.Validation.mapWithNever mapFn
|
|
1432
|
-
, isMatchCandidate = parsed.isMatchCandidate
|
|
1433
|
-
}
|
|
1434
|
-
)
|
|
1435
|
-
(\_ -> [])
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
{-| -}
|
|
1439
|
-
combine :
|
|
1440
|
-
(parsed -> combined)
|
|
1441
|
-
->
|
|
1442
|
-
Form
|
|
1443
|
-
error
|
|
1444
|
-
{ combineAndView
|
|
1445
|
-
| combine : Form.Validation.Validation error parsed kind constraints
|
|
1446
|
-
}
|
|
1447
|
-
input
|
|
1448
|
-
msg
|
|
1449
|
-
-> ServerForms error combined
|
|
1450
|
-
-> ServerForms error combined
|
|
1451
|
-
combine mapFn form (ServerForms serverForms) =
|
|
1452
|
-
ServerForms (serverForms ++ [ normalizeServerForm mapFn form ])
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
{-| -}
|
|
1456
|
-
initCombinedServer :
|
|
1457
|
-
(parsed -> combined)
|
|
1458
|
-
->
|
|
1459
|
-
Form
|
|
1460
|
-
error
|
|
1461
|
-
{ combineAndView
|
|
1462
|
-
| combine : Combined error (BackendTask backendTaskError (Form.Validation.Validation error parsed kind constraints))
|
|
1463
|
-
}
|
|
1464
|
-
input
|
|
1465
|
-
msg
|
|
1466
|
-
-> ServerForms error (BackendTask backendTaskError (Form.Validation.Validation error combined kind constraints))
|
|
1467
|
-
initCombinedServer mapFn serverForms =
|
|
1468
|
-
initCombined (BackendTask.map (Form.Validation.map mapFn)) serverForms
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
{-| -}
|
|
1472
|
-
combineServer :
|
|
1473
|
-
(parsed -> combined)
|
|
1474
|
-
->
|
|
1475
|
-
Form
|
|
1476
|
-
error
|
|
1477
|
-
{ combineAndView
|
|
1478
|
-
| combine :
|
|
1479
|
-
Combined error (BackendTask backendTaskError (Form.Validation.Validation error parsed kind constraints))
|
|
1480
|
-
}
|
|
1481
|
-
input
|
|
1482
|
-
msg
|
|
1483
|
-
-> ServerForms error (BackendTask backendTaskError (Form.Validation.Validation error combined kind constraints))
|
|
1484
|
-
-> ServerForms error (BackendTask backendTaskError (Form.Validation.Validation error combined kind constraints))
|
|
1485
|
-
combineServer mapFn a b =
|
|
1486
|
-
combine (BackendTask.map (Form.Validation.map mapFn)) a b
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
{-| -}
|
|
1490
|
-
type alias StyledHtmlForm error parsed data msg =
|
|
1491
|
-
Form
|
|
1492
|
-
error
|
|
1493
|
-
{ combine : Combined error parsed
|
|
1494
|
-
, view : Context error data -> List (Html.Styled.Html (PagesMsg msg))
|
|
1495
|
-
}
|
|
1496
|
-
data
|
|
1497
|
-
msg
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
{-| -}
|
|
1501
|
-
type Form error combineAndView input userMsg
|
|
1502
|
-
= Form
|
|
1503
|
-
(RenderOptions userMsg)
|
|
1504
|
-
(List ( String, FieldDefinition ))
|
|
1505
|
-
(Maybe input
|
|
1506
|
-
-> FormState
|
|
1507
|
-
->
|
|
1508
|
-
{ result : Dict String (List error)
|
|
1509
|
-
, isMatchCandidate : Bool
|
|
1510
|
-
, combineAndView : combineAndView
|
|
1511
|
-
}
|
|
1512
|
-
)
|
|
1513
|
-
(input -> List ( String, Maybe String ))
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
type alias RenderOptions userMsg =
|
|
1517
|
-
{ submitStrategy : SubmitStrategy
|
|
1518
|
-
, method : Method
|
|
1519
|
-
, onSubmit : Maybe ({ fields : List ( String, String ) } -> userMsg)
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
{-| -}
|
|
1524
|
-
type Method
|
|
1525
|
-
= Post
|
|
1526
|
-
| Get
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
methodToString : Method -> String
|
|
1530
|
-
methodToString method =
|
|
1531
|
-
case method of
|
|
1532
|
-
Post ->
|
|
1533
|
-
"POST"
|
|
1534
|
-
|
|
1535
|
-
Get ->
|
|
1536
|
-
"GET"
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
{-| -}
|
|
1540
|
-
type SubmitStrategy
|
|
1541
|
-
= FetcherStrategy
|
|
1542
|
-
| TransitionStrategy
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
{-| -}
|
|
1546
|
-
type FieldDefinition
|
|
1547
|
-
= RegularField
|
|
1548
|
-
| HiddenField
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
{-| -}
|
|
1552
|
-
addErrorsInternal : String -> List error -> Dict String (List error) -> Dict String (List error)
|
|
1553
|
-
addErrorsInternal name newErrors allErrors =
|
|
1554
|
-
allErrors
|
|
1555
|
-
|> Dict.update name
|
|
1556
|
-
(\errors ->
|
|
1557
|
-
Just (newErrors ++ (errors |> Maybe.withDefault []))
|
|
1558
|
-
)
|