Let’s say you have a form that has three fields:
percentOne
percentTwo
percentThree
You want to declare some validation constraints to ensure that the total of percentOne
, percentTwo
and percentThree
total 100.
The first thing I would do is set up the default constraints for each field, to ensure they each receive a number between 0 and 100 like this:
this.constraints = {
"percentOne": { required: true, type="numeric", range: "0..100" },
"percentTwo": { required: true, type="numeric", range: "0..100" },
"percentThree": { required: true, type="numeric", range: "0..100" }
}
I can see a few different ways to ensure the total of all three fields equals 100:
Approach 1: Add a UDF validator on one of the fields:
this.constraints = {
"percentOne": { required: true, type="numeric", range: "0..100" },
"percentTwo": { required: true, type="numeric", range: "0..100" },
"percentThree": {
required: true,
type="numeric",
range: "0..100",
// UDF to check all three fields
udf: function( event, target ) {
// skip this check if fields are not numeric or missing
if (
isNull( getPercentOne() ) ||
!isNumeric( getPercentOne() ) ||
isNull( getPercentTwo() ) ||
!isNumeric( getPercentTwo() ) ||
isNull( getPercentThree() ) ||
!isNumeric( getPercentThree() )
) {
return true;
}
// assert: we have numeric values for all three fields
return ( getPercentOne() + getPercentTwo() = getPercentThree() == 100 )
},
udfMessage: "Percentages must total 100"
}
}
This approach smells funny to me because the constraint exists on a field, but pertains to multiple fields. I feel like there must be a more elegant way.
Approach 2: Virtual Attribute / Calculated Property
Another idea I had is to use a virtual attribute (for Quick fans) or a calculated property that isn’t persisted.
property="percentTotal" setter="false" persistent=false // calculated property or non persisted quick property
this.constraints = {
"percentOne": { required: true, type="numeric", range: "0..100" },
"percentTwo": { required: true, type="numeric", range: "0..100" },
"percentThree": { required: true, type="numeric", range: "0..100"},
"percentTotal": { discrete = "eq:100" }
}
function getPercentTotal() { // do the math or return null }
I like the virtual attribute approach better because it looks the cleanest. What do you think? Would you tackle this problem in a different way?