Skip to content

Commit f884b7e

Browse files
committed
simplified formValidation so that there is no need to call fullValidate explicitely anymore
1 parent 3ce6507 commit f884b7e

File tree

4 files changed

+74
-51
lines changed

4 files changed

+74
-51
lines changed

core/src/main/scala/torstenrudolf/scalajs/react/formbinder/Macros.scala

Lines changed: 65 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import japgolly.scalajs.react.{BackendScope, Callback, ReactComponentB, ReactNod
44
import japgolly.scalajs.react.vdom.prefix_<^._
55

66
import scala.scalajs.js
7+
import scala.util.{Failure, Success, Try}
78

89

910
object Macros {
@@ -311,13 +312,13 @@ case class FormFieldBinding[O](formFieldDescriptor: FormFieldDescriptor[O],
311312

312313
def currentValidatedValue: Option[O] = formFieldBackend.flatMap(_.currentValidatedValue)
313314

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))
315316

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)))
317318

318-
def clear: Callback = formFieldBackend.map(_.clear).getOrElse(Callback.info("backend not set yet"))
319+
def clear: Try[Callback] = Try(formFieldBackend.get.clear)
319320

320-
def resetToDefault: Callback = formFieldBackend.map(_.resetToDefault).getOrElse(Callback.info("backend not set yet"))
321+
def resetToDefault: Try[Callback] = Try(formFieldBackend.get.resetToDefault)
321322

322323
private val formFieldComp = FormField[O](this)
323324

@@ -343,67 +344,96 @@ trait FormAPI[T] extends Form[T] {
343344
protected var isInitializing: Boolean
344345
protected val formLayout: FormLayout[T]
345346

347+
private var _validatedFormData: Option[T] = None
348+
private var _formGlobalValidationResult: Option[ValidationResult] = None
349+
346350
protected def globalValidator(data: T): ValidationResult
347351

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]
354353

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+
}
357373

358-
override def fullValidate: Callback = {
359-
validateAllFields(showUninitializedError = true)
360374
}
361375

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())
363378

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)
370383
}
371384

372-
override def validatedFormData: Option[T] = validatedCurrentData._1
385+
override def validatedFormData: Option[T] = {
386+
validate(showUninitializedError = true)
387+
_validatedFormData
388+
}
373389

374390
protected def allFormFieldBindings: List[FormFieldBinding[_]]
375391

376-
// private def allFieldValidationResults: List[ValidationResult] = allFormFieldBindings.map(_.currentValidationResult)
377-
378392
override def field[A](fd: torstenrudolf.scalajs.react.formbinder.FormFieldDescriptor[A]): ReactNode =
379393
fieldBinding(fd).formField
380394

381395
def fieldBinding[A](fd: torstenrudolf.scalajs.react.formbinder.FormFieldDescriptor[A]): torstenrudolf.scalajs.react.formbinder.FormFieldBinding[A]
382396

383397
override def fieldValue[A](fd: FormFieldDescriptor[A]): Option[A] = fieldBinding(fd).currentValidatedValue
384398

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+
}
386403

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+
}
388412

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+
}
390417

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+
}
392424

393425
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+
}
400430

401431
private var forceGlobalValidationMessageUpdate: Option[Callback] = None
402432

403433
override def globalValidationMessage: ReactNode = {
404434
val component = ReactComponentB[Unit]("FormGlobalError")
405435
.stateless
406-
.render(scope => <.div(validatedCurrentData._2.errorMessage))
436+
.render(scope => <.div(_formGlobalValidationResult.nonEmpty ?= <.div(_formGlobalValidationResult.get.errorMessage)))
407437
.componentDidMount(scope => Callback {forceGlobalValidationMessageUpdate = Some(scope.forceUpdate)})
408438
.build
409439

demo/src/main/scala/torstenrudolf/scalajs/react/formbinder/demo/components/InterdependentFieldsMuiDemo.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,9 @@ object InterdependentFieldsMuiDemo {
6969

7070
// use it like this:
7171
def handleSubmit: Callback = {
72-
form.fullValidate >> {
73-
form.validatedFormData match {
74-
case Some(data) => Callback.alert(s"do what you need to do with $data")
75-
case _ => Callback.empty
76-
}
72+
form.validatedFormData match {
73+
case Some(data) => Callback.alert(s"do what you need to do with $data")
74+
case _ => Callback.empty
7775
}
7876
}
7977

demo/src/main/scala/torstenrudolf/scalajs/react/formbinder/demo/components/SimpleDemo.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,9 @@ object SimpleDemo {
5252

5353
// use it like this:
5454
def handleSubmit: Callback = {
55-
form.fullValidate >> {
56-
form.validatedFormData match {
57-
case Some(data) => Callback.alert(s"do what you need to do with $data")
58-
case _ => Callback.empty
59-
}
55+
form.validatedFormData match {
56+
case Some(data) => Callback.alert(s"do what you need to do with $data")
57+
case _ => Callback.empty
6058
}
6159
}
6260

demo/src/main/scala/torstenrudolf/scalajs/react/formbinder/demo/components/SimpleMuiDemo.scala

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,14 @@ object SimpleMuiDemo {
4545

4646
// use it like this:
4747
def handleSubmit: Callback = {
48-
form.fullValidate >> {
49-
form.validatedFormData match {
50-
case Some(data) => Callback.alert(s"do what you need to do with $data")
51-
case _ => Callback.empty
52-
}
48+
form.validatedFormData match {
49+
case Some(data) => Callback.alert(s"do what you need to do with $data")
50+
case _ => Callback.empty
5351
}
5452
}
5553

5654
// you have full control over the display of the form fields
5755
def render() = {
58-
println(form.globalValidationMessage)
5956
CodeExample(code, "Using Material-UI")(
6057
<.div(
6158
<.form(

0 commit comments

Comments
 (0)