Skip to content

Validate init #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions examples/address/Address.res
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ module AddressMilitary = {
module Segment = {
type t = [#Unit | #Community | #Postal ]
let all: array<t> = [#Unit, #Community, #Postal ]

external show: t => string = "%identity"
external fromStringUnsafe: string => t = "%identity"

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

let init: Field.input = Street({
street: Some("333"),
city: Some("333"),
state: Some(#Alabama),
zip: Some("33")
})

@react.component
let make = (~onSubmit) => {
let form = Form.use(.
~context=contextDefault,
~init=None,
~validateInit=false,
~init=Some(Validate(init)),
)

let handleSubmit = React.useMemo1( () => {
Expand Down
3 changes: 1 addition & 2 deletions examples/everything/Everything.res
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ let init: Field.input = [
let make = (~onSubmit) => {
let form = Form.use(.
~context=Addresses.contextDefault,
~init=Some(init),
~validateInit=true,
~init=Some(Validate(init)),
)

let handleSubmit = React.useMemo1( () => {
Expand Down
3 changes: 1 addition & 2 deletions examples/login/Login.res
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,9 @@ module Input = {

@react.component
let make = (~onSubmit) => {
let form = Form.use(.
let form = Form.use(.
~context=contextValidate,
~init=None,
~validateInit=false,
)

let handleSubmit = React.useMemo1( () => {
Expand Down
56 changes: 55 additions & 1 deletion src/Field.res
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,60 @@ You'll see this Field.T in the Module Functions which asserts
that a module passed to the function has each of these types and values.
")

module Init = {
@deriving(accessors)
type t<'a> = Validate('a) | Natural('a)
let map = (t, fn) => {
switch t {
| Validate(a) => Validate(fn(a))
| Natural(a) => Natural(fn(a))
}
}

let get = t => {
switch t {
| Validate(a) => a
| Natural(a) => a
}
}

let toValidate = (t: t<'a>) => {
switch t {
| Validate(a) => Some(a)
| Natural(_) => None
}
}

let isValidate = t => t->toValidate->Option.isSome

let toNatural = (t: t<'a>) => {
switch t {
| Validate(_) => None
| Natural(a) => Some(a)
}
}

let toNatural = t => t->toNatural->Option.isSome

let collectOption = (t: t<'a>, fn: 'a => option<'b>): option<t<'b>> => {
switch t {
| Validate(a) => fn(a)->Option.map(validate)
| Natural(a) => fn(a)->Option.map(natural)
}
}

let distributeOption = collectOption(_, x=>x)

let collectArray = (t: t<'a>, fn: 'a => array<'b>): array<t<'b>> => {
switch t {
| Validate(a) => fn(a)->Array.map(validate)
| Natural(a) => fn(a)->Array.map(natural)
}
}

let distributeArray = collectArray(_, x=>x)
}

module type T = {
@ocamldoc("A field is passed a context to its validate and reduce methods
and it can be any shape of your choosing.
Expand Down Expand Up @@ -85,7 +139,7 @@ module type T = {
There may be cases where you _DO_ want to emit to dyn, like adding an element to FieldArray, but
needs to be handled in Array - AxM
")
let makeDyn: (context, option<input>, Rxjs.Observable.t<input>, option<Rxjs.Observable.t<()>>) =>
let makeDyn: (context, option<Init.t<input>>, Rxjs.Observable.t<input>, option<Rxjs.Observable.t<()>>) =>
Dyn.t<Close.t<Form.t<t, actions<()>>>>

// Accessors for input, output, etc via the Field
Expand Down
42 changes: 24 additions & 18 deletions src/FieldArray.res
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
prefer(#Busy, Store.busy, inner),
preferFiltered(#Invalid, Store.invalid(_, #Part), inner, inner->I.filter),
preferFiltered(#Dirty, Store.dirty, inner, inner->I.filter),
prefer(#Init, Store.init, inner),
validate(inner),
]
->Array.reduce(Result.first, Error(#Invalid))
Expand Down Expand Up @@ -275,7 +276,6 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
})
}


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

let traverseSetk = (context: context, set, elements, validate) => {
let traverseSetk = (context: context, set, elements: Array.t<Field.Init.t<F.input>>, validate) => {
elements
->Array.mapi( (value, index) => (value, index))
->traverseTuple3( ((value, index)) => {
Expand Down Expand Up @@ -329,20 +329,21 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
)
)

let makeDynInner = (context: context, initial: option<input>, set: Rxjs.Observable.t<input>, val: Option.t<Rxjs.Observable.t<()>>)
let makeDynInner = (context: context, initial: option<Field.Init.t<input>>, set: Rxjs.Observable.t<input>, val: Option.t<Rxjs.Observable.t<()>>)
: (
Array.t<Close.t<Form.t<(key, F.t), F.actions<unit>>>>,
Array.t<Dyn.init<Close.t<Form.t<(key, F.t), F.actions<unit>>>>>,
Array.t<Dyn.dyn<Close.t<Form.t<(key, F.t), F.actions<()>>>>>,
)
=> {
Option.first(initial, Option.flap0(context.empty))
->Option.or([])
Option.first(initial, Option.flap0(context.empty)->Option.map(x => Field.Init.Natural(x)))
->Option.or(Field.Init.Natural([]))
->Field.Init.distributeArray
->traverseSetk(context, set, _, val)
->packKey
}

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

let applyInner = (inners): Rxjs.Observable.t<Close.t<Form.t<t, actions<()>>>> => {
let applyInner = (~validateFn, inners): Rxjs.Observable.t<Close.t<Form.t<t, actions<()>>>> => {
let inners = mergeInner(inners)
inners.pack.field
->makeStore(~validate=validateImpl(context, false))
->makeStore(~validate=validateFn)
->Dynamic.map(applyField(inners))
}

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

let init = {
let validateInit = initial->Option.map(Field.Init.isValidate)->Option.or(false)
let validateFn = validateImpl(context, validateInit)
combineLatestScan(initInner, firstInner)
->Dynamic.tap(Rxjs.next(stateValues))
->Dynamic.switchMap(applyInner)
->Dynamic.switchMap(applyInner(~validateFn))
}

let set = Rxjs.merge3(setOuter, setInner, setOpt)
Expand Down Expand Up @@ -461,18 +464,18 @@ module Make: Make = (F: Field.T, I: IArray with type t = F.t) => {
, _sequence)
: ( Array.t<Close.t<Form.t<(key, F.t), F.actions<unit>>>>
, Array.t<Dyn.init<Close.t<Form.t<(key, F.t), F.actions<unit>>>>>
, Array.t
< Either.t
< (Dyn.init<Close.t<Form.t<(key, F.t), F.actions<unit>>>>, Dyn.dyn<Close.t<Form.t<(key, F.t), F.actions<()>>>>)
, Array.t<
Either.t<
(Dyn.init<Close.t<Form.t<(key, F.t), F.actions<unit>>>>, Dyn.dyn<Close.t<Form.t<(key, F.t), F.actions<()>>>>)
, Dyn.dyn<Close.t<Form.t<(key, F.t), F.actions<()>>>>
>
>
>
)
=> {
// We are tracking the init of dynamically added elements in the 'dyn' value.
// When a new array level change comes in, we no longer want to replay the init,
// So cast all the eithers to right, losing the inits
let dyns = dyns->Array.map(Either.either(x => x->Tuple.snd2->Either.right, Either.right))
// let dyns = Array.map(dyns, y => Either.either(x => x->Tuple.snd2->Either.right, x => Either.right(x), y))

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

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

let dyn =

let dyn = {
let validateFn = validateImpl(context, false)
dynInner
->Dynamic.map(Dynamic.tap(_, Rxjs.next(stateValues)))
->Dynamic.switchMap(Dynamic.map(_, applyInner))
->Dynamic.switchMap(Dynamic.map(_, applyInner(~validateFn)))
->Rxjs.pipe(Rxjs.takeUntil(complete))
}

{first, init, dyn}
}
Expand Down
4 changes: 2 additions & 2 deletions src/FieldCheck.res
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ let mapActions: (actions<'a>, 'a => 'b) => actions<'b> = (actions, fn) => {
set: input => input->actions.set->fn
}

let makeDyn = (context: context, initial: option<input>, setOuter: Rxjs.Observable.t<input>, _validate: option<Rxjs.Observable.t<()>> )
let makeDyn = (context: context, initial: option<Field.Init.t<input>>, setOuter: Rxjs.Observable.t<input>, _validate: option<Rxjs.Observable.t<()>> )
: Dyn.t<Close.t<Form.t<t, actions<()>>>>
=> {
let field =
initial
->Option.map(set)
->Option.map(x => x->Field.Init.get->set)
->Option.or(init(context))

let complete = Rxjs.Subject.makeEmpty()
Expand Down
Loading