Link Search Menu Expand Document

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 as passImmediatelyIf in this use case. The difference between skip and pass is that bcx-validation actually has 3 states for result: pass, fail and skip (considered neither pass nor fail). The final result generated by validation.validate(...) only contains error literals, the tri-state was not exposed to end user. State skip was designed to properly implement conditional validation (the if 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.