Validating form with mutually exclusive fields with AngularJS

I'm creating a web application using AngularJS, and i have a form with two fields that should be validated as mutually exclusive. I mean either one or the other field should be filled, but not both (it is also valid if neither is filled). My form (simplified model of the real case) is like this:

<form id="form1" name="form1">
   <p>
      <input type="text" id="field1" name="field1" ng-model="model.item1" not-both-filled="model.item2" />
   </p>
   <p>
      <input type="text" id="field2" name="field2" ng-model="model.item2" not-both-filled="model.item1" />
   </p>
   <p ng-show="form1.$invalid">not valid</p>
   <p ng-show="!form1.$invalid">valid</p>
</form>

and i created a directive like this:

angular.module('myApp', []).directive('notBothFilled', function() {
    return {
        require: 'ngModel',
        link: function(scope, elem, attrs, ctrl) {
            var validateNotBothFilled = function(value) {
                var theOther = scope.$eval(attrs.notBothFilled);
                var isNotValid = (value && value !== '' && theOther && theOther !== '');
                ctrl.$setValidity('field1', !isNotValid);
                ctrl.$setValidity('field2', !isNotValid);
                return value;
            };

            ctrl.$parsers.unshift(validateNotBothFilled);
            ctrl.$formatters.unshift(validateNotBothFilled);

        }
    };
});

However, this has the following problem:

  1. fill field 1
  2. fill field 2
  3. form becomes invalid
  4. empty field 1
  5. form should become valid but does not

If i empty field2 instead, the behaviour is correct.

Sorry i still have very little AngularJS experience (and Englis is not my native language), but could someone tell me:

  1. Is directive like this is generally the correct approach to validate mutually exclusive fields in AngularJS?
  2. What is wrong with my code, how can i get the form valid again if i empty any of the fields?

I also have a JS fiddle: http://jsfiddle.net/k6ps/7vCZg/

Sincerely, k6ps

Answers


Your main problem is these 2 lines:

ctrl.$setValidity('field1', !isNotValid);
ctrl.$setValidity('field2', !isNotValid);

These 2 lines actually set 2 custom validity of the same input field, you need to somehow get the ngModelController of the other input field to set its validity

Try this:

<form id="form1" name="form1">
    <p>
        <input type="text" id="field1" name="field1" ng-model="model.item1" not-both-filled="field2" form="form1"/> 
    //I pass the field name instead and also the form where these inputs are defined.
    </p>
    <p>
        <input type="text" id="field2" name="field2" ng-model="model.item2" not-both-filled="field1" form="form1"/>
    </p>
    <p ng-show="form1.$invalid">not valid</p>
    <p ng-show="!form1.$invalid">valid</p>
</form>

JS:

angular.module('myApp', []).directive('notBothFilled', function() {
    return {
        require: 'ngModel',
        link: function(scope, elem, attrs, ctrl) {
            var validateNotBothFilled = function(value) {
                var form = scope[attrs.form];
                var theOther = form[attrs.notBothFilled];
                var isNotValid = (value && value !== '' && theOther.$modelValue && theOther.$modelValue !== '');
                ctrl.$setValidity('field', !isNotValid);//set validity of the current element
                theOther.$setValidity('field', !isNotValid);//set validity of the other element.
                return value;
            };

            ctrl.$parsers.unshift(validateNotBothFilled);

        }
    };
});

DEMO


Need Your Help