Chapter 8
Implementing a custom validation directive
Now we have our tests in place, so we can implement the functionality of
the directive:
myModule.directive('validateEquals', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ngModelCtrl) {
function validateEqual(myValue) {
var valid = (myValue === scope.$eval(attrs.validateEquals));
ngModelCtrl.$setValidity('equal', valid);
return valid? myValue : undefined;
}
ngModelCtrl.$parsers.push(validateEqual);
ngModelCtrl.$formatters.push(validateEqual);
scope.$watch(attrs.validateEquals, function() {
ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue);
});
}
};
});
We create a function called validateEqual(value), which compares the passed
in value with the value of the expression. We push this into the $parsers and
$formatters pipelines, so that the validation function gets called each time either
the model or the view changes.
In this directive we also have to take into account the model we are comparing
against changing. We do this by setting up a watch on the expression, which we
retrieve from the attrs parameter of the linking function. When it does change,
we artificially trigger the $parsers pipeline to run by calling $setViewValue().
This ensures that all potential $parsers are run in case any of them modify the
model value before it gets to our validator.
Creating an asynchronous model validator
Some validation can only be done by interacting with a remote service, say a
database. In these cases, the response from the service will be asynchronous.
This brings in complication not only in working with model validation
asynchronously but also in testing this functionality.