@@ -118,9 +118,10 @@ object Macros {
118
118
$defaultFormValue.map((dmv: $dataModelTypeSymbol)=> Map[String, Any](.. ${dataModelFields.map(fn => (fn.asTerm.name.toString, q " dmv. ${fn.asTerm.name}" ))})).getOrElse( ${getDefaultValuesFromCompanionObject(c)(dataModelCompanion)})
119
119
""" )
120
120
121
- val formFieldDescriptors = formLayout.info.members.map(_.asTerm).filter(_.isAccessor)
121
+ val formFieldDescriptors = formLayout.info.members.sorted
122
+ .map(_.asTerm)
123
+ .filter(_.isAccessor)
122
124
.filter(_.asMethod.returnType.<:< (typeOf[FormFieldDescriptor [_]]))
123
- .toList
124
125
125
126
// check that targetFields and formFieldDescriptors match
126
127
val missing = dataModelFields.map(_.name.toString).toSet.diff(formFieldDescriptors.map(_.name.toString).toSet)
@@ -130,8 +131,15 @@ object Macros {
130
131
131
132
// check types
132
133
val nonMatchingFieldTypes = formFieldDescriptors.toSet.diff(
133
- formFieldDescriptors.filter(x =>
134
- List (dataModelFields.find(_.name == x.name).get.info) == x.info.resultType.typeArgs).toSet)
134
+ formFieldDescriptors.filter(ffd =>
135
+ ffd.info.resultType.typeArgs.size == 1 && {
136
+ dataModelFields.find(_.name == ffd.name) match {
137
+ case Some (targetField) => targetField.info == ffd.info.resultType.typeArgs.head
138
+ case None => ffd.info.resultType.typeArgs.head == typeOf[Unit ] // is calculated field
139
+ }
140
+ }
141
+ ).toSet
142
+ )
135
143
136
144
if (nonMatchingFieldTypes.nonEmpty) {
137
145
c.abort(
@@ -187,18 +195,21 @@ object Macros {
187
195
globalTargetValidator.map(v => q " $v" )
188
196
.getOrElse(q " (d: $dataModelTypeSymbol) => torstenrudolf.scalajs.react.formbinder.ValidationResult.Success " )
189
197
190
- case class CompoundField (targetField : c.universe.Symbol ,
191
- defaultValueExpr : c.universe.Tree ,
198
+ case class CompoundField (targetField : Option [c.universe.Symbol ],
192
199
transformedTargetFieldValidator : c.universe.Tree ,
193
200
name : String ,
194
201
termName : c.universe.TermName ,
195
202
formFieldDescriptor : c.universe.TermSymbol ) {
196
- val fieldType = targetField.info
197
- val formFieldBindingValDef = q " val $termName: torstenrudolf.scalajs.react.formbinder.FormFieldBinding[ ${targetField.info}] "
203
+ val hasTargetField = targetField.isDefined
204
+ val fieldType = targetField.map(_.info).getOrElse(typeOf[Unit ])
205
+ val isString = fieldType =:= typeOf[String ]
206
+ val formFieldBindingValDef = q " val $termName: torstenrudolf.scalajs.react.formbinder.FormFieldBinding[ $fieldType] "
207
+ val defaultValueExpr = q " $targetFieldDefaultValues.get( $name).asInstanceOf[Option[ ${fieldType}]] "
198
208
}
199
209
200
- val compoundFields = dataModelFields.zipWithIndex.map { case (f, idx) =>
201
- val fieldName = f.name.toString
210
+ val compoundFields = formFieldDescriptors.zipWithIndex.map{ case (ffd, idx) =>
211
+ val fieldName = ffd.name.toString
212
+ val dataModelField = dataModelFields.find(_.name.toString == ffd.name.toString)
202
213
203
214
val validatorAndParamsOpt = targetFieldValidatorsInfo.get(fieldName)
204
215
@@ -217,15 +228,15 @@ object Macros {
217
228
.map(v => q " Some( $v) " ).getOrElse(q " None " )
218
229
219
230
CompoundField (
220
- targetField = f,
221
- defaultValueExpr = q " $targetFieldDefaultValues.get( $fieldName).asInstanceOf[Option[ ${f.info}]] " ,
231
+ targetField = dataModelField,
222
232
transformedTargetFieldValidator = transformedTargetFieldValidator,
223
233
name = fieldName,
224
234
termName = TermName (fieldName),
225
- formFieldDescriptor = formFieldDescriptors.find(_.name.toString == fieldName).get
235
+ formFieldDescriptor = ffd
226
236
)
227
237
}
228
238
239
+
229
240
val newTree =
230
241
q """
231
242
new torstenrudolf.scalajs.react.formbinder.Form[ $dataModelTypeSymbol] with torstenrudolf.scalajs.react.formbinder.FormAPI[ $dataModelTypeSymbol] {
@@ -237,25 +248,25 @@ object Macros {
237
248
override var isInitializing: Boolean = true
238
249
239
250
private case class FieldBindingsHolder(.. ${compoundFields.map(_.formFieldBindingValDef)})
240
- private val fieldBindingsHolder = new FieldBindingsHolder(.. ${compoundFields.map(f => q """ torstenrudolf.scalajs.react.formbinder.FormFieldBinding[ ${f.fieldType}]( $formLayout. ${f.formFieldDescriptor}, ${f.defaultValueExpr}, ${f.targetField.info =:= typeOf[ String ] }, ${f.transformedTargetFieldValidator}, this, ${f.name}) """ )})
251
+ private val fieldBindingsHolder = new FieldBindingsHolder(.. ${compoundFields.map(f => q """ torstenrudolf.scalajs.react.formbinder.FormFieldBinding[ ${f.fieldType}]( $formLayout. ${f.formFieldDescriptor}, ${f.defaultValueExpr}, ${f.isString }, ${f.transformedTargetFieldValidator}, this, ${f.name} , ${f.hasTargetField }) """ )})
241
252
242
253
val allFormFieldBindings: List[torstenrudolf.scalajs.react.formbinder.FormFieldBinding[_]] = ${compoundFields.map(f => q " fieldBindingsHolder. ${f.termName}" )}
243
254
244
255
isInitializing = false
245
256
onChangeCB.runNow() // update default values
246
257
247
258
override def currentValueWithoutGlobalValidation: scala.util.Try[ $dataModelTypeSymbol] = {
248
- val fieldValues = allFormFieldBindings .map(_.currentValidatedValue)
259
+ val fieldValues = allFormFieldBindingsWithUnderlyingDataField .map(_.currentValidatedValue)
249
260
if (fieldValues.forall(_.nonEmpty)) {
250
- val d = $dataModelCompanion.apply(.. ${compoundFields.map(f => q " fieldBindingsHolder. ${f.termName}.currentValidatedValue.get " )})
261
+ val d = $dataModelCompanion.apply(.. ${compoundFields.filter(_.hasTargetField). map(f => q " ${f.termName} = fieldBindingsHolder. ${f.termName}.currentValidatedValue.get " )})
251
262
scala.util.Success(d)
252
263
} else {
253
264
scala.util.Failure(torstenrudolf.scalajs.react.formbinder.FormUninitialized)
254
265
}
255
266
}
256
267
257
268
override def setModelValue(newModelValue: $dataModelTypeSymbol): japgolly.scalajs.react.Callback = {
258
- ${compoundFields.map(f =>
269
+ ${compoundFields.filter(_.hasTargetField). map(f =>
259
270
q """ fieldBindingsHolder. ${f.termName}.updateValue(newModelValue. ${f.termName}) match {
260
271
case scala.util.Success(cb) => cb
261
272
case scala.util.Failure(e) => throw torstenrudolf.scalajs.react.formbinder.FormUninitialized
@@ -314,6 +325,8 @@ object FormField {
314
325
$.modState(_.copy(currentValidationResult = Some (validationResult), showUnitializedError = showUnitializedErrorX)) >> CallbackTo (validationResult)
315
326
}
316
327
328
+ def forceUpdate : Callback = $.forceUpdate
329
+
317
330
private def onChangeCB : Callback =
318
331
$.state.zip($.props) >>= { case (state, props) => props.onChangeCB(currentValidatedValue) }
319
332
@@ -379,14 +392,17 @@ case class FormFieldBinding[O](formFieldDescriptor: FormFieldDescriptor[O],
379
392
private val isString : Boolean ,
380
393
transformedTargetFieldValidator : Option [(O , FormAPI [_]) => ValidationResult ],
381
394
private val parentForm : Form [_] with FormAPI [_],
382
- fieldName : String ) {
395
+ fieldName : String ,
396
+ hasTargetField : Boolean ) {
383
397
384
398
def currentValidatedValue : Option [O ] = formFieldBackend.flatMap(_.currentValidatedValue)
385
399
386
400
def currentValidationResult : Try [ValidationResult ] = Try (formFieldBackend.get.currentValidationResult)
387
401
388
402
def validate (showUninitializedError : Boolean ): Try [Callback ] = Try (formFieldBackend.get.validate(showUninitializedError).void)
389
403
404
+ def forceUpdateComponent : Callback = formFieldBackend.map(_.forceUpdate).getOrElse(Callback .empty)
405
+
390
406
def updateValue (v : O ): Try [Callback ] = Try (formFieldBackend.get.updateRawValue(Some (v)))
391
407
392
408
def clear : Try [Callback ] = Try (formFieldBackend.get.clear)
@@ -452,7 +468,16 @@ trait FormAPI[T] extends Form[T] {
452
468
}
453
469
454
470
private def validateAllFields (showUninitializedError : Boolean ): Try [Unit ] =
455
- Try (allFormFieldBindings.map(_.validate(showUninitializedError = showUninitializedError).get).reduce {_ >> _}.runNow())
471
+ // this validates the fields as well as triggers the field components to refresh
472
+ Try (
473
+ {
474
+ allFormFieldBindingsWithUnderlyingDataField
475
+ .map(_.validate(showUninitializedError = showUninitializedError).get) ++
476
+ allFormFieldBindings.filter(f => ! f.hasTargetField).map(_.forceUpdateComponent)
477
+ }
478
+ .reduce {_ >> _}
479
+ .runNow()
480
+ )
456
481
457
482
override def fullValidate : Callback = Callback {validate(showUninitializedError = true )}
458
483
@@ -467,14 +492,17 @@ trait FormAPI[T] extends Form[T] {
467
492
_validatedFormData
468
493
}
469
494
470
- private def allFieldValidationResults : List [ValidationResult ] = allFormFieldBindings .map(
495
+ private def allFieldValidationResults : List [ValidationResult ] = allFormFieldBindingsWithUnderlyingDataField .map(
471
496
_.currentValidationResult match {
472
497
case Success (vr) => vr
473
498
case _ => throw FormUninitialized
474
499
})
475
500
476
501
protected def allFormFieldBindings : List [FormFieldBinding [_]]
477
502
503
+ protected def allFormFieldBindingsWithUnderlyingDataField : List [FormFieldBinding [_]] =
504
+ allFormFieldBindings.filter(_.hasTargetField)
505
+
478
506
override def field [A ](fd : torstenrudolf.scalajs.react.formbinder.FormFieldDescriptor [A ]): ReactNode =
479
507
fieldBinding(fd).formField
480
508
@@ -490,7 +518,7 @@ trait FormAPI[T] extends Form[T] {
490
518
override def clearAllFields : Callback = {
491
519
// note: this calls FormAPI.onChangeCB and therefore FormAPI.validate N times (N = number of form fields)
492
520
// this could become slow for big forms and we might need to improve this
493
- Try (allFormFieldBindings .map(_.clear.get).reduce {_ >> _}) match {
521
+ Try (allFormFieldBindingsWithUnderlyingDataField .map(_.clear.get).reduce {_ >> _}) match {
494
522
case Success (cb) => cb
495
523
case Failure (e) => throw FormUninitialized
496
524
}
@@ -501,12 +529,13 @@ trait FormAPI[T] extends Form[T] {
501
529
case Failure (e) => throw FormUninitialized
502
530
}
503
531
504
- override def resetAllFields : Callback = Try (allFormFieldBindings.map(_.resetToDefault.get).reduce {_ >> _}) match {
505
- // note: this calls FormAPI.onChangeCB and therefore FormAPI.validate N times (N = number of form fields)
506
- // this could become slow for big forms and we might need to improve this
507
- case Success (cb) => cb
508
- case Failure (e) => throw FormUninitialized
509
- }
532
+ override def resetAllFields : Callback =
533
+ Try (allFormFieldBindingsWithUnderlyingDataField.map(_.resetToDefault.get).reduce {_ >> _}) match {
534
+ // note: this calls FormAPI.onChangeCB and therefore FormAPI.validate N times (N = number of form fields)
535
+ // this could become slow for big forms and we might need to improve this
536
+ case Success (cb) => cb
537
+ case Failure (e) => throw FormUninitialized
538
+ }
510
539
511
540
override def setFieldValue [A ](fd : torstenrudolf.scalajs.react.formbinder.FormFieldDescriptor [A ], value : A ): Callback =
512
541
fieldBinding(fd).updateValue(value) match {
0 commit comments