Reputation: 3790
I'm working on understanding how to use Angular Directives to implement front-end validations. While I'm familiar with the way that directives generally work, what I'm having a hard time finding in any tutorial, blogpost, or even the actual Angular docs, is how to meaningfully implement a useful validation. (By that, I mean one that isn't already there, as in this basic tutorial for how to implement a validation attribute.
The most common of these in my experience, is a 'Required If' scenario. If I have Text Field A and Text Field B in my form, and Text Field A has a value, my business rules tell me that Text Field B must have a value. However, the various tutorials for validation directives have all only relied on the element that they are tied to.
Question: I suspect that I am approaching the problem of how to implement something like a Required If validation completely wrong. In the Angular way, what is the correct way to require a value in a form if and only if a field that it's dependent on has a value?
Upvotes: 1
Views: 191
Reputation: 3790
I did some additional experimentation today. A coworker prompted me with a different situation, that finally led to me solving the problem for myself.
I was fundamentally looking at the problem in the wrong way, for starters - specifically, I was looking at it in the jQuery way, by expecting the directive to somehow expose ways to read individual form elements. This isn't necessarily correct, since we have a scope that we can evaluate.
Here is the pre-1.3 Angular directive code:
var app = angular.module('app', []);
app.controller('someController', [
'$scope',
function($scope) {
$scope.valueA = '';
$scope.valueB = 'Chicken';
}
]);
app.directive('requiredIf', [
function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attr, model) {
// Read the companion attribute 'otherModelName'
var otherModelName = attr.otherModelName;
scope.$watch(attr.ngModel, function (value) {
var isValid = checkAgainstDependentModel(value);
model.$setValidity('requiredIf', isValid);
});
function checkAgainstDependentModel(value) {
// Assumes that the scope exposes a property with the passed-in
// model name as its name
var field = scope[otherModelName];
var isValid = true;
if(field != null || field != '')
if(value == null || value == '')
isValid = false;
return isValid;
}
}
};
}
]);
...In markup we would use it like so:
<form name='someForm'>
<input type='text' name='mainField' ng-model='valueA' />
<input type='text' name='subordinateField' ng-model='valueB' required-if other-model-name='valueA' />
<span style='color=red' ng-if='someForm.subordinateField.$error.requiredIf'>
(Required!)
</span>
</form>
This pattern can be expanded to various other custom validations, pre-1.3. My research has shown me that Angular 1.3 will remove the $parsers
and $formatters
in favor of $validators
and $asyncValidators
.
EDIT: Instead of using $formatters/$parsers
, a better idea I ran across is to instead do a scope.$watch
on the associated ngModel
. Since this is a pre-1.3 implementation, we can still just do model.$setValidity('thing', isValid);
This is based on the answer to the question of how best to achieve remote validation pre-1.3.
Upvotes: 1
Reputation: 288
You could try using ng-if to add the next form element only if the previous form element is valid. The second element won't exist in the DOM unless the first element is valid.
Upvotes: 1