Chain of rules
You can use multiple rules to validate a value. bcx-validation
will merge all error messages into one single array.
validation.validate('lorem', [
{validate: /[a-z]/, message: 'must contain lower case letter'},
{validate: /[A-Z]/, message: 'must contain upper case letter'},
{validate: /\d/, message: 'must contain digit'},
]);
// => ['must contain upper case letter', 'must contain digit']
When bcx-validation
validates the value, you have chance to break the chain (stop it early) using reserved keys stopValidationChainIfFail
and stopValidationChainIfPass
. Here the 3rd rule is skipped because of failure on the 2nd rule.
validation.validate('lorem', [
{validate: /[a-z]/, message: 'must contain lower case letter', stopValidationChainIfFail: true},
{validate: /[A-Z]/, message: 'must contain upper case letter', stopValidationChainIfFail: true},
{validate: /\d/, message: 'must contain digit'},
]);
// => ['must contain upper case letter']
bcx-validation
also provides three validators to support easy early break of chain. passImmediatelyIf
, skipImmediatelyIf
, failImmediatelyIf
.
var rule = [
{validate: 'passImmediatelyIf', value: "$value == 'NA'"},
{validate: 'failImmediatelyIf', value: '_.isEmpty($value)', message: 'must not be empty'},
{validate: /[a-z]/, message: 'must contain lower case letter', stopValidationChainIfFail: true},
{validate: /[A-Z]/, message: 'must contain upper case letter', stopValidationChainIfFail: true},
{validate: /\d/, message: 'must contain digit'},
];
validation.validate('NA', rule);
// => undefined
validation.validate('', rule);
// => ['must not be empty']
Here the first validation checks if value is "NA"
, stop the validation chain immediately and return as passed. The second validation checks if value is empty, stop the validation chain immediately and fail with message “must not be empty”.
We kept rest of the rule unchanged, but you can rewrite the third rule as
{validate: "failImmediatelyIf", value => !/[a-z]/.test(v), message: "must contain lower case letter"}
.
You can use
skipImmediatelyIf
in first validation. It behaves same aspassImmediatelyIf
in this use case. The difference betweenskip
andpass
is thatbcx-validation
actually has 3 states for result:pass
,fail
andskip
(considered neither pass nor fail). The final result generated byvalidation.validate(...)
only contains error literals, the tri-state was not exposed to end user. Stateskip
was designed to properly implement conditional validation (theif
transformer).
Since chain of rules is considered a rule, you can use sub-chain inside a chain. Beware early break of sub-chain doesn’t affect the outer chain. Here the last rule was still checked after early break in previous sub-chain.
var rule = [
{validate: 'passImmediatelyIf', value: "$value == 'NA'"},
{validate: 'failImmediatelyIf', value: '_.isEmpty($value)', message: 'must not be empty'},
[
{validate: /[a-z]/, message: 'must contain lower case letter', stopValidationChainIfFail: true},
{validate: /[A-Z]/, message: 'must contain upper case letter', stopValidationChainIfFail: true},
{validate: /\d/, message: 'must contain digit'}
],
{validate: /_/, message: 'must contain underscore'}
];
validation.validate('a', rule);
// => [ 'must contain upper case letter', 'must contain underscore' ]
With the introduction of chain control, it looks getting complicated. But fortunately, you will rarely use any of passImmediatelyIf
, skipImmediatelyIf
, failImmediatelyIf
, stopValidationChainIfFail
or stopValidationChainIfPass
. They are meant to be used in defining new validator with composition.
Let’s move on to conditional validation.