Angular directive with scope.$watch to force validation of other fields
angularjs, angularjs-directive, javascript, validation
Solution
I would do it by explicitly validating the $viewValue inside the `$watch`:
PLUNKER
app.directive("matchModel", [
function () {
return {
require: "ngModel",
link: function (scope, element, attributes, ngModelController) {
var valueCache = null;
scope.$watch(attributes["matchModel"], function (newValue, oldValue) {
valueCache = newValue;
validate(ngModelController.$viewValue);
});
var validate = function (value) {
ngModelController.$setValidity("match", value === valueCache);
return value === valueCache ? value : undefined;
};
ngModelController.$parsers.unshift(validate);
}
};
}]);
Problem
I've written a `match-model` Angular directive that I use for password/password-repeat process when users register in my application. Password repeat field has this particular attribute that validates this field against original password field. My directive has `scope.$watch` for optimization purposes because I don't have to read related scope property value each time I validate my repeat password scope property but I rather just use cached value which changes when related scope property value changes (original password). This is my directive: ``` .directive("matchModel", ["$timeout", function ($timeout) { return { require: "ngModel", link: function (scope, element, attributes, ngModelController) { var valueCache = null; // watch "match-model" model property value scope.$watch(attributes["matchModel"], function (newValue, oldValue) { valueCache = newValue; /* scope.$apply(); // error $digest in progress $timeout(function () { scope.$digest(); }); // no error, not working $timeout(function () { scope.$apply(); }); // no error, not working */ }); // add model validation parser ngModelController.$parsers.unshift(function (value) { ngModelController.$setValidity("match", value === valueCache); return value === valueCache ? value : undefined; }); } }; }]); ``` My form consists of two fields (that are relevant for this question): ``` <input type="password" name="password" id="password" placeholder="password" class="validate" autocomplete="off" required ng-minlength="6" ng-model="data.password" /> <input type="password" name="passwordRepeat" id="passwordRepeat" placeholder="repeat password" class="validate" autocomplete="off" required ng-model="data.passwordRepeat" match-model="data.password" /> ``` Requirements - When user enters first password, field becomes valid when enough characters are entered - in above case that's 6 characters - when user enters second password, field should become valid when data matches first password - if user returns to first field and changes original password, second field should invalidate How it currently works 1 and 2 work as expected, but 3 doesn't. That's why I wanted to add `scope.$digest` to propagate scope model changes to other fields. And `scope.$watch` is the right moment because it executes when that particular scope model property changes. It seems that `scope.$digest` (or `scope.$apply` for that matter) doesn't validate model. Validation doesn't seem to be executed along with it. Question So how should I do something like `scope.$validate` or even better `element.$validate` so it would only validate my particular field instead of the whole model (resulting in invalid form in the UI).