rule-set-validation v0.3.3
Angular RuleSet Validation
Super light-weight Angular form validation!
WARNING: This module is intended as a sort of monkey patch to get around Angular 1.2.x's lack of native form validation on blur.
You should use a later version of Angular where possible and use ng-model-options="{ updateOn: 'blur' }"
, or try ui.utils' validate & event binder.
In the case that neither of these options suit you, go nuts with RuleSet Validation.
The skinny of it
RuleSet validation is driven by native Angular form validation. $dirty/$pristine still apply, form.$valid still applies, but the validation functions are handled by RuleSet Validation using what you feed into the rsStore
.
Why wrap native validation?
For two reasons:
As mentioned before, Angular 1.2.x's
ngModel
will update on input/keypress/whatever, with no native options to change that. This means that validation runs with every key press the user entered, and it's terrible to receive a validation message before you've even finished typing in the form field. RuleSet Validation runs only on blur (but I would consider making that configurable if wanted).Angular validation does not correlate specific errors to error messages, meaning you have to do something like this:
<div data-ng-show="myForm.myEmail.$error.required"> Please enter an email </div> <div data-ng-show="myForm.myEmail.$error.email"> Please enter a valid email address </div> <!-- And so on... -->
And pollute your view with several error messages per validated form input. RuleSet Validation will append a
message
string to yourformField.$error
object so you can simply use:<div data-ng-show="myForm.myEmail.$error.message"> {{ myForm.myEmail.$error.message }} </div>
Whereby the
message
string will be remove from theformField.$error
object once the field is valid. This message is defined in your rule set and attached to the validation function that belongs to the message.
But how do I use RuleSet Validation?
Why you simply have to register a rule set and use the data-rule-set-validate
directive on your form inputs! RuleSet Validation will use the name attribute to identify related rule sets (so make sure you name things consistently!) and expects the form input to be wrapped in a form with a name (which again, should match your rule sets), as well as using ngModel to identify and set the state of the input (dirty/pristine etc). Without these three attributes, Rule Set validation will not work
Oh and don't forget to include the module in your app:
angular.module('myApp', ['ruleSetValidation']);
Configuring rule sets
Rule sets can be configured by injecting the rsStore
object in your app.run/controller/service and using rsStore.addRule(ruleSet)
to add the rule set to the store.
Rule sets must be objects that follow this pattern:
{
myForm: {
myEmail: {
"you need to enter an email address": function(value) {
return value !== '';
},
"your email is blacklisted": function(value){
return ['bademail@baddomain.com'].indexOf(value);
}
}
}
}
where myForm
is the name of the form and myEmail
is the name/ngModel value of the of the input you are validating.
If you fail to adhere to these object standards, you will most likely see run-time errors. I'm not going to baby-proof a monkey patch, deal with it.
The rule storage will also not handle duplicates. If you register a rule already defined, the new one will take it's place.
Group rule sets
Group rule sets are defined similar to regular rule sets, except the validation function takes in an object that will feature the group items:
{
myForm: {
myPassword: {
"Please enter password": rsRules.required,
},
myConfirmPassword: {
"Please confirm password": rsRules.required,
}
myPasswordGroup: {
"Passwords must match": function(values) {
return values.myPassword === values.myConfirmPassword;
}
}
}
}
<input
name="myPassword"
data-ng-model="myPassword"
data-rule-set-validate
data-rule-set-validate-group="myPasswordGroup"
/>
<input
name="myConfirmPassword"
data-ng-model="myConfirmPassword"
data-rule-set-validate
data-rule-set-validate-group="myPasswordGroup"
/>
<div data-ng-show="myForm.myPasswordGroup.$error.message">
{{ myForm.myPasswordGroup.$error.message }}
</div>
Group validation will be called whenever a blur event is fired on any of the inputs marked with data-rule-set-validate-group
, so make sure you check for empty strings or undefined where relevant! Rules will be determined by the value of data-rule-set-validate-group
. Inputs that belong to multiple groups should comma seperate their group names (without spaces), like so:
<input
name="myField"
data-ng-model="myField"
data-rule-set-validate
data-rule-set-validate-group="myGroup,myOtherGroup"
/>
Asynchronous Validation
Asynchronous validation utilizes $q.defer()
. RuleSet validation will give your rules the defer object and your rules need to return its promise
. RuleSet Validation expects you to resolve
or reject
with a plain boolean representing the validity. Resolving or rejecting does not change the validation outcome; for instance you can use deferred.reject(true)
, and the input will be marked as valid, or deferred.resolve(false)
and the input will be marked as invalid.
To use asynchronous validation, label your rule set object with ':async'
:
{
myForm: {
'myAddress:async': {
"Invalid address": function(value, deferred){
$http.post(
//... post url/data
).success(function(){
// a success code represents valid input, so we resolve with 'true'
deferred.resolve(true);
}).error(function(){
// a failure code represents invalid input, so we reject with 'false'
deferred.reject(false);
});
return deferred.promise;
},
}
}
}
You can even mix group & asynchronous validation. Just tag your group rule with ':async'
!.
Form validation
If you need to validate the whole form using the rule set in js, simply call rsFormValidator.validate(formCtrl)
, where formCtrl
is the form controller (i.e scope.myForm
). RuleSet Validation will simply loop through all the rules you've associated with that form and call the validation, allowing you to check myForm.$valid
for form validity*.
Associated validation
RuleSet validation provides a facility to revalidate other fields as well as the current one. For example, if you need to revalidate the email field when a checkbox is checked, add data-rule-set-revalidate="myEmailField"
to the checkbox, and the email field will be revalidated when the checkbox is toggled. the revalidate directive can accept a comma seperated list of fields to revalidate upon change.
Caveats
DO NOT mix native angular form validation with RuleSet Validation. RuleSet Validation DOES NOT use
ctrl.$parsers
and as such will not run with default angular validation. There are some default rules that you can use that will cover the basics that angular normally provides. Just injectrsRules
when you define your rule set:// ... var myRules = { myEmail: { "Please enter an email address": rsRules.required, "Please enter a valid email address": rsRules.email, "your email is blacklisted": function(value){ return ['bademail@baddomain.com'].indexOf(value); } } } // ...
Alternatively, you can use
rsStore.rsRules
to access the default rules provided. Check the src for more rules.*
rsFormValidator.validate
will begin asynchronous validation but will not notify you when it has been completed. If you need those hooks, build them into your rule sets, at least for now.