Skip to content

Commit 5b8122a

Browse files
committed
ValidateInit
Summary: Test Plan: *** Fix FieldVector makeStore to return Invalid for Invalid part, not Busy. *** Add Init state propogation to FieldVector(Product)
1 parent 7ad1a16 commit 5b8122a

16 files changed

+251
-162
lines changed

examples/address/Address.res

+9-3
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ module AddressMilitary = {
165165
module Segment = {
166166
type t = [#Unit | #Community | #Postal ]
167167
let all: array<t> = [#Unit, #Community, #Postal ]
168-
168+
169169
external show: t => string = "%identity"
170170
external fromStringUnsafe: string => t = "%identity"
171171

@@ -413,12 +413,18 @@ module Input = {
413413
// Create a hook for running this field
414414
module Form = UseField.Make(Field)
415415

416+
let init: Field.input = Street({
417+
street: Some("333"),
418+
city: Some("333"),
419+
state: Some(#Alabama),
420+
zip: Some("33")
421+
})
422+
416423
@react.component
417424
let make = (~onSubmit) => {
418425
let form = Form.use(.
419426
~context=contextDefault,
420-
~init=None,
421-
~validateInit=false,
427+
~init=Some(Validate(init)),
422428
)
423429

424430
let handleSubmit = React.useMemo1( () => {

examples/everything/Everything.res

+1-2
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@ let init: Field.input = [
9696
let make = (~onSubmit) => {
9797
let form = Form.use(.
9898
~context=Addresses.contextDefault,
99-
~init=Some(init),
100-
~validateInit=true,
99+
~init=Some(Validate(init)),
101100
)
102101

103102
let handleSubmit = React.useMemo1( () => {

examples/login/Login.res

+1-2
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,9 @@ module Input = {
8080

8181
@react.component
8282
let make = (~onSubmit) => {
83-
let form = Form.use(.
83+
let form = Form.use(.
8484
~context=contextValidate,
8585
~init=None,
86-
~validateInit=false,
8786
)
8887

8988
let handleSubmit = React.useMemo1( () => {

src/Field.res

+55-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,60 @@ You'll see this Field.T in the Module Functions which asserts
44
that a module passed to the function has each of these types and values.
55
")
66

7+
module Init = {
8+
@deriving(accessors)
9+
type t<'a> = Validate('a) | Natural('a)
10+
let map = (t, fn) => {
11+
switch t {
12+
| Validate(a) => Validate(fn(a))
13+
| Natural(a) => Natural(fn(a))
14+
}
15+
}
16+
17+
let get = t => {
18+
switch t {
19+
| Validate(a) => a
20+
| Natural(a) => a
21+
}
22+
}
23+
24+
let toValidate = (t: t<'a>) => {
25+
switch t {
26+
| Validate(a) => Some(a)
27+
| Natural(_) => None
28+
}
29+
}
30+
31+
let isValidate = t => t->toValidate->Option.isSome
32+
33+
let toNatural = (t: t<'a>) => {
34+
switch t {
35+
| Validate(_) => None
36+
| Natural(a) => Some(a)
37+
}
38+
}
39+
40+
let toNatural = t => t->toNatural->Option.isSome
41+
42+
let collectOption = (t: t<'a>, fn: 'a => option<'b>): option<t<'b>> => {
43+
switch t {
44+
| Validate(a) => fn(a)->Option.map(validate)
45+
| Natural(a) => fn(a)->Option.map(natural)
46+
}
47+
}
48+
49+
let distributeOption = collectOption(_, x=>x)
50+
51+
let collectArray = (t: t<'a>, fn: 'a => array<'b>): array<t<'b>> => {
52+
switch t {
53+
| Validate(a) => fn(a)->Array.map(validate)
54+
| Natural(a) => fn(a)->Array.map(natural)
55+
}
56+
}
57+
58+
let distributeArray = collectArray(_, x=>x)
59+
}
60+
761
module type T = {
862
@ocamldoc("A field is passed a context to its validate and reduce methods
963
and it can be any shape of your choosing.
@@ -85,7 +139,7 @@ module type T = {
85139
There may be cases where you _DO_ want to emit to dyn, like adding an element to FieldArray, but
86140
needs to be handled in Array - AxM
87141
")
88-
let makeDyn: (context, option<input>, Rxjs.Observable.t<input>, option<Rxjs.Observable.t<()>>) =>
142+
let makeDyn: (context, option<Init.t<input>>, Rxjs.Observable.t<input>, option<Rxjs.Observable.t<()>>) =>
89143
Dyn.t<Close.t<Form.t<t, actions<()>>>>
90144

91145
// Accessors for input, output, etc via the Field

src/FieldArray.res

+24-18
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
202202
prefer(#Busy, Store.busy, inner),
203203
preferFiltered(#Invalid, Store.invalid(_, #Part), inner, inner->I.filter),
204204
preferFiltered(#Dirty, Store.dirty, inner, inner->I.filter),
205+
prefer(#Init, Store.init, inner),
205206
validate(inner),
206207
]
207208
->Array.reduce(Result.first, Error(#Invalid))
@@ -275,7 +276,6 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
275276
})
276277
}
277278

278-
279279
external outputToString: output => string = "%identity"
280280
let show = (store: t) => {
281281
`FieldArray{
@@ -288,7 +288,7 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
288288
}`
289289
}
290290

291-
let traverseSetk = (context: context, set, elements, validate) => {
291+
let traverseSetk = (context: context, set, elements: Array.t<Field.Init.t<F.input>>, validate) => {
292292
elements
293293
->Array.mapi( (value, index) => (value, index))
294294
->traverseTuple3( ((value, index)) => {
@@ -329,20 +329,21 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
329329
)
330330
)
331331

332-
let makeDynInner = (context: context, initial: option<input>, set: Rxjs.Observable.t<input>, val: Option.t<Rxjs.Observable.t<()>>)
332+
let makeDynInner = (context: context, initial: option<Field.Init.t<input>>, set: Rxjs.Observable.t<input>, val: Option.t<Rxjs.Observable.t<()>>)
333333
: (
334334
Array.t<Close.t<Form.t<(key, F.t), F.actions<unit>>>>,
335335
Array.t<Dyn.init<Close.t<Form.t<(key, F.t), F.actions<unit>>>>>,
336336
Array.t<Dyn.dyn<Close.t<Form.t<(key, F.t), F.actions<()>>>>>,
337337
)
338338
=> {
339-
Option.first(initial, Option.flap0(context.empty))
340-
->Option.or([])
339+
Option.first(initial, Option.flap0(context.empty)->Option.map(x => Field.Init.Natural(x)))
340+
->Option.or(Field.Init.Natural([]))
341+
->Field.Init.distributeArray
341342
->traverseSetk(context, set, _, val)
342343
->packKey
343344
}
344345

345-
let makeDyn = (context: context, initial: option<input>, setOuter: Rxjs.Observable.t<input>, validate: option<Rxjs.Observable.t<()>>)
346+
let makeDyn = (context: context, initial: option<Field.Init.t<input>>, setOuter: Rxjs.Observable.t<input>, validate: option<Rxjs.Observable.t<()>>)
346347
: Dyn.t<Close.t<Form.t<t, actions<()>>>>
347348
=> {
348349
// Every observable has a complete, to terminate the stream
@@ -395,10 +396,10 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
395396
, close: makeClose(close)
396397
}
397398

398-
let applyInner = (inners): Rxjs.Observable.t<Close.t<Form.t<t, actions<()>>>> => {
399+
let applyInner = (~validateFn, inners): Rxjs.Observable.t<Close.t<Form.t<t, actions<()>>>> => {
399400
let inners = mergeInner(inners)
400401
inners.pack.field
401-
->makeStore(~validate=validateImpl(context, false))
402+
->makeStore(~validate=validateFn)
402403
->Dynamic.map(applyField(inners))
403404
}
404405

@@ -425,9 +426,11 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
425426
->Rxjs.pipe(Rxjs.distinct())
426427

427428
let init = {
429+
let validateInit = initial->Option.map(Field.Init.isValidate)->Option.or(false)
430+
let validateFn = validateImpl(context, validateInit)
428431
combineLatestScan(initInner, firstInner)
429432
->Dynamic.tap(Rxjs.next(stateValues))
430-
->Dynamic.switchMap(applyInner)
433+
->Dynamic.switchMap(applyInner(~validateFn))
431434
}
432435

433436
let set = Rxjs.merge3(setOuter, setInner, setOpt)
@@ -461,18 +464,18 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
461464
, _sequence)
462465
: ( Array.t<Close.t<Form.t<(key, F.t), F.actions<unit>>>>
463466
, Array.t<Dyn.init<Close.t<Form.t<(key, F.t), F.actions<unit>>>>>
464-
, Array.t
465-
< Either.t
466-
< (Dyn.init<Close.t<Form.t<(key, F.t), F.actions<unit>>>>, Dyn.dyn<Close.t<Form.t<(key, F.t), F.actions<()>>>>)
467+
, Array.t<
468+
Either.t<
469+
(Dyn.init<Close.t<Form.t<(key, F.t), F.actions<unit>>>>, Dyn.dyn<Close.t<Form.t<(key, F.t), F.actions<()>>>>)
467470
, Dyn.dyn<Close.t<Form.t<(key, F.t), F.actions<()>>>>
468-
>
469471
>
472+
>
470473
)
471474
=> {
472475
// We are tracking the init of dynamically added elements in the 'dyn' value.
473476
// When a new array level change comes in, we no longer want to replay the init,
474477
// So cast all the eithers to right, losing the inits
475-
let dyns = dyns->Array.map(Either.either(x => x->Tuple.snd2->Either.right, Either.right))
478+
// let dyns = Array.map(dyns, y => Either.either(x => x->Tuple.snd2->Either.right, x => Either.right(x), y))
476479

477480
switch change {
478481
| #Clear => {
@@ -489,7 +492,7 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
489492
| #Add(value) => {
490493
let index = stateValues->Array.length
491494
let setElement = set->Dynamic.keepMap(Array.get(_, index))
492-
let {first, init, dyn} = F.makeDyn(context.element, value, setElement, None)
495+
let {first, init, dyn} = F.makeDyn(context.element, value->Option.map(Field.Init.natural), setElement, None)
493496
let key = getKey()
494497
let first = packKeyValue(key, first)
495498
let init = packKeyObss(key, init)
@@ -516,7 +519,7 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
516519
}
517520
| #Set(input) => {
518521
stateValues->Array.forEach(c => c.close())
519-
let (values, obs, dyns) = input->traverseSetk(context, set, _, validate)->packKey
522+
let (values, obs, dyns) = input->Array.map(Field.Init.natural)->traverseSetk(context, set, _, validate)->packKey
520523

521524
let dyns = dyns->Array.mapi( (dyn, i) => (obs->Array.getUnsafe(i), dyn)->Either.left)
522525
( values, obs, dyns)
@@ -585,11 +588,14 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
585588
}}
586589
})
587590

588-
let dyn =
591+
592+
let dyn = {
593+
let validateFn = validateImpl(context, false)
589594
dynInner
590595
->Dynamic.map(Dynamic.tap(_, Rxjs.next(stateValues)))
591-
->Dynamic.switchMap(Dynamic.map(_, applyInner))
596+
->Dynamic.switchMap(Dynamic.map(_, applyInner(~validateFn)))
592597
->Rxjs.pipe(Rxjs.takeUntil(complete))
598+
}
593599

594600
{first, init, dyn}
595601
}

src/FieldCheck.res

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ let mapActions: (actions<'a>, 'a => 'b) => actions<'b> = (actions, fn) => {
2929
set: input => input->actions.set->fn
3030
}
3131

32-
let makeDyn = (context: context, initial: option<input>, setOuter: Rxjs.Observable.t<input>, _validate: option<Rxjs.Observable.t<()>> )
32+
let makeDyn = (context: context, initial: option<Field.Init.t<input>>, setOuter: Rxjs.Observable.t<input>, _validate: option<Rxjs.Observable.t<()>> )
3333
: Dyn.t<Close.t<Form.t<t, actions<()>>>>
3434
=> {
3535
let field =
3636
initial
37-
->Option.map(set)
37+
->Option.map(x => x->Field.Init.get->set)
3838
->Option.or(init(context))
3939

4040
let complete = Rxjs.Subject.makeEmpty()

0 commit comments

Comments
 (0)