[CBValidation 4.4.0] Exploring Best Practices When Validating a Calculation of Multiple Fields

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?