Computed values¶
KForm supports the definition of schemas whose values must be the result of a provided computation.
Such schemas are called “computed schemas”, instances of
ComputedSchema and
the values they represent are named “computed values”, instances of
ComputedValue.
Computed values are part of a form’s model. As such, in a client application, they are managed by a form manager, who is responsible for updating their value based on the defined computation.
Computed schemas further contain an implicit validation which forbids the value they represent from differing from the result of the computation. I.e. forms containing computed schemas cannot be submitted unless the values represented by said schemas match the result of their computations.
Let’s take a look at an example of how to define a computed value. In the precious sections, we defined the model and schema of a form for purchasing bus tickets. Let us suppose that our form should now also include the total price to pay for the trip:
data class BusTripForm(
var email: String,
var passengers: Table<Passenger>,
var totalPrice: Double,
)
data class Passenger(var name: String, var age: Int?)
If we say that the total price to pay is a fixed amount per person, we can define a computed value for computing the total amount with the following definition:
object TotalPrice : ComputedValue<Double>() {
private const val PRICE_PER_PASSENGER = 10.0
private val ComputedValueContext.passengers: Table<Passenger> by dependency("/passengers")
override suspend fun ComputedValueContext.compute(): Double =
passengers.size * PRICE_PER_PASSENGER
}
Finally, we can declare the totalPrice field as a computed schema whose computation is defined by
TotalPrice:
val BusTripFormSchema = ClassSchema {
BusTripForm::email { StringSchema(/*…*/ ) }
BusTripForm::passengers {
TableSchema(/*…*/ ) {
ClassSchema {
Passenger::name { StringSchema(/*…*/ ) }
Passenger::age { NullableSchema(/*…*/ ) { IntSchema(/*…*/ ) } }
}
}
}
BusTripForm::totalPrice { ComputedSchema(TotalPrice) { DoubleSchema() } }
}
Computed values vs validations¶
As you might have noticed, the way of defining a computed value is very similar to the way of
defining a validation. In fact, they both extend the same
Computation interface. The main
differences between both are:
- The class being extended:
ComputedValueinstead ofValidation. - The type of the computation context:
ComputedValueContextinstead ofValidationContext. The main difference being that there’s no notion of “current value” in a computed value since that’s what’s actually being computed. - The method to implement:
computeinstead ofvalidate. This method should return the computed value itself. - There’s no
dependsOnDescendantsproperty available, as it would be nonsensical (we cannot depend on what is currently being defined).
Aside from these differences, computed values are able to declare dependencies on other values or external contexts in the same way validations do. For reference, check the following validations’ documentation:
- Depending on other values: the rules on how to declare dependencies using delegated properties also apply to computed values.
- Depending on descendants: except for the portion on
dependsOnDescendants, the same care must be taken when depending on descendants of dependencies. - Depending on external context: the rules on how to depend on external contexts also apply to computed values.
Built-in computed values¶
KForm offers the following built-in computed value: