@@ -4,6 +4,7 @@ import japgolly.scalajs.react.{BackendScope, Callback, ReactComponentB, ReactNod
4
4
import japgolly .scalajs .react .vdom .prefix_<^ ._
5
5
6
6
import scala .scalajs .js
7
+ import scala .util .{Failure , Success , Try }
7
8
8
9
9
10
object Macros {
@@ -311,13 +312,13 @@ case class FormFieldBinding[O](formFieldDescriptor: FormFieldDescriptor[O],
311
312
312
313
def currentValidatedValue : Option [O ] = formFieldBackend.flatMap(_.currentValidatedValue)
313
314
314
- def validate (showUninitializedError : Boolean ): Callback = formFieldBackend.map(_ .validate(showUninitializedError)).getOrElse( Callback .info( " backend not set yet " ))
315
+ def validate (showUninitializedError : Boolean ): Try [ Callback ] = Try ( formFieldBackend.get .validate(showUninitializedError))
315
316
316
- def updateValue (v : O ): Callback = formFieldBackend.map(_ .updateRawValue(Some (v))).getOrElse( Callback .info( " backend not set yet " ))
317
+ def updateValue (v : O ): Try [ Callback ] = Try ( formFieldBackend.get .updateRawValue(Some (v)))
317
318
318
- def clear : Callback = formFieldBackend.map(_ .clear).getOrElse( Callback .info( " backend not set yet " ) )
319
+ def clear : Try [ Callback ] = Try ( formFieldBackend.get .clear)
319
320
320
- def resetToDefault : Callback = formFieldBackend.map(_ .resetToDefault).getOrElse( Callback .info( " backend not set yet " ) )
321
+ def resetToDefault : Try [ Callback ] = Try ( formFieldBackend.get .resetToDefault)
321
322
322
323
private val formFieldComp = FormField [O ](this )
323
324
@@ -343,67 +344,96 @@ trait FormAPI[T] extends Form[T] {
343
344
protected var isInitializing : Boolean
344
345
protected val formLayout : FormLayout [T ]
345
346
347
+ private var _validatedFormData : Option [T ] = None
348
+ private var _formGlobalValidationResult : Option [ValidationResult ] = None
349
+
346
350
protected def globalValidator (data : T ): ValidationResult
347
351
348
- private def validate (data : T ): (Option [T ], ValidationResult ) = {
349
- globalValidator(data) match {
350
- case r if ! r.isValid => (None , r)
351
- case _ => (Some (data), ValidationResult .Success )
352
- }
353
- }
352
+ protected def currentUnvalidated : scala.util.Try [T ]
354
353
355
- private def validateAllFields (showUninitializedError : Boolean ): Callback =
356
- allFormFieldBindings.map(_.validate(showUninitializedError = showUninitializedError)).reduce {_ >> _}
354
+ private def validate (showUninitializedError : Boolean ): Unit = {
355
+ val fieldValidationSucceeded = validateAllFields(showUninitializedError = showUninitializedError) match {
356
+ case Success (_) => currentUnvalidated match {
357
+ case Success (data) => globalValidator(data) match {
358
+ case r if ! r.isValid =>
359
+ _validatedFormData = None
360
+ _formGlobalValidationResult = Some (r)
361
+ case _ =>
362
+ _validatedFormData = Some (data)
363
+ _formGlobalValidationResult = Some (ValidationResult .Success )
364
+ }
365
+ case Failure (e) =>
366
+ _validatedFormData = None
367
+ _formGlobalValidationResult = None
368
+ }
369
+ case Failure (_) =>
370
+ _validatedFormData = None
371
+ _formGlobalValidationResult = None
372
+ }
357
373
358
- override def fullValidate : Callback = {
359
- validateAllFields(showUninitializedError = true )
360
374
}
361
375
362
- protected def currentUnvalidated : scala.util.Try [T ]
376
+ private def validateAllFields (showUninitializedError : Boolean ): Try [Unit ] =
377
+ Try (allFormFieldBindings.map(_.validate(showUninitializedError = showUninitializedError).get).reduce {_ >> _}.runNow())
363
378
364
- private def validatedCurrentData : (Option [T ], ValidationResult ) = {
365
- // todo: optimize so we don't run this unnecessarily
366
- currentUnvalidated match {
367
- case scala.util.Success (d) => validate(d)
368
- case scala.util.Failure (f) => (None , ValidationResult .Success ) // ValidationResult.withError("Please fill in required fields."))
369
- }
379
+ override def fullValidate : Callback = Callback {validate(showUninitializedError = true )}
380
+
381
+ def onChangeCB : Callback = {
382
+ Callback (validate(showUninitializedError = false )) >> forceGlobalValidationMessageUpdate.getOrElse(Callback .empty)
370
383
}
371
384
372
- override def validatedFormData : Option [T ] = validatedCurrentData._1
385
+ override def validatedFormData : Option [T ] = {
386
+ validate(showUninitializedError = true )
387
+ _validatedFormData
388
+ }
373
389
374
390
protected def allFormFieldBindings : List [FormFieldBinding [_]]
375
391
376
- // private def allFieldValidationResults: List[ValidationResult] = allFormFieldBindings.map(_.currentValidationResult)
377
-
378
392
override def field [A ](fd : torstenrudolf.scalajs.react.formbinder.FormFieldDescriptor [A ]): ReactNode =
379
393
fieldBinding(fd).formField
380
394
381
395
def fieldBinding [A ](fd : torstenrudolf.scalajs.react.formbinder.FormFieldDescriptor [A ]): torstenrudolf.scalajs.react.formbinder.FormFieldBinding [A ]
382
396
383
397
override def fieldValue [A ](fd : FormFieldDescriptor [A ]): Option [A ] = fieldBinding(fd).currentValidatedValue
384
398
385
- override def clearField [A ](fd : FormFieldDescriptor [A ]): Callback = fieldBinding(fd).clear
399
+ override def clearField [A ](fd : FormFieldDescriptor [A ]): Callback = fieldBinding(fd).clear match {
400
+ case Success (cb) => cb
401
+ case Failure (e) => throw FormUninitialized
402
+ }
386
403
387
- override def clearAllFields : Callback = allFormFieldBindings.map(_.clear).reduce {_ >> _}
404
+ override def clearAllFields : Callback = {
405
+ // note: this calls FormAPI.onChangeCB and therefore FormAPI.validate N times (N = number of form fields)
406
+ // this could become slow for big forms and we might need to improve this
407
+ Try (allFormFieldBindings.map(_.clear.get).reduce {_ >> _}) match {
408
+ case Success (cb) => cb
409
+ case Failure (e) => throw FormUninitialized
410
+ }
411
+ }
388
412
389
- override def resetFieldToDefault [A ](fd : FormFieldDescriptor [A ]): Callback = fieldBinding(fd).resetToDefault
413
+ override def resetFieldToDefault [A ](fd : FormFieldDescriptor [A ]): Callback = fieldBinding(fd).resetToDefault match {
414
+ case Success (cb) => cb
415
+ case Failure (e) => throw FormUninitialized
416
+ }
390
417
391
- override def resetAllFields : Callback = allFormFieldBindings.map(_.resetToDefault).reduce {_ >> _}
418
+ override def resetAllFields : Callback = Try (allFormFieldBindings.map(_.resetToDefault.get).reduce {_ >> _}) match {
419
+ // note: this calls FormAPI.onChangeCB and therefore FormAPI.validate N times (N = number of form fields)
420
+ // this could become slow for big forms and we might need to improve this
421
+ case Success (cb) => cb
422
+ case Failure (e) => throw FormUninitialized
423
+ }
392
424
393
425
override def setFieldValue [A ](fd : torstenrudolf.scalajs.react.formbinder.FormFieldDescriptor [A ], value : A ): Callback =
394
- fieldBinding(fd).updateValue(value)
395
-
396
- def onChangeCB : Callback = {
397
- validateAllFields(showUninitializedError = false ) >>
398
- forceGlobalValidationMessageUpdate.getOrElse(Callback .empty)
399
- }
426
+ fieldBinding(fd).updateValue(value) match {
427
+ case Success (cb) => cb
428
+ case Failure (e) => throw FormUninitialized
429
+ }
400
430
401
431
private var forceGlobalValidationMessageUpdate : Option [Callback ] = None
402
432
403
433
override def globalValidationMessage : ReactNode = {
404
434
val component = ReactComponentB [Unit ](" FormGlobalError" )
405
435
.stateless
406
- .render(scope => < .div(validatedCurrentData._2. errorMessage))
436
+ .render(scope => < .div(_formGlobalValidationResult.nonEmpty ?= < .div(_formGlobalValidationResult.get. errorMessage) ))
407
437
.componentDidMount(scope => Callback {forceGlobalValidationMessageUpdate = Some (scope.forceUpdate)})
408
438
.build
409
439
0 commit comments