Using the AngularJS 'ng-required' attribute to make fields co-dependent
One of the bugs I had to fix recently was around ensuring that if Field X was populated, then Field Y became required. In the example I was working with this was all the more complicated due to there being repeated fieldsets on the same page, each of which needed to have the co-dependency between fields but only within their own fieldset.
Getitng this working relies on using the AngularJS ng-required attribute to determine whether each field in the form is valid, or not. Here's a very simple exaple that shows the two fields in the screenshot above and makes it so that if one is populated, the other must also be populated for the form to be valid:
<form name="codepfields" style="font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;"> Field 1: <input name="input1" ng-model="vm.data.input1" ng-required="vm.data.input2 != '' && vm.data.input2 != undefined"> <br /> Field 2: <input name="input2" ng-model="vm.data.input2" ng-required="vm.data.input1 != '' && vm.data.input1 != undefined"> <br /> <button ng-style="codepfields.$valid ? {'color': 'green'} :{'color': 'red'}" ng-disabled="!codepfields.$valid">Clickety!</button> <br /> <br /> <div> Valid: {{codepfields.$valid}} <br /> Input1: '{{vm.data.input1}}' (Blank: {{vm.data.input1 == ''}}, Undefined: {{vm.data.input1 == undefined}}) <br /> Input2: '{{vm.data.input2}}' (Blank: {{vm.data.input2 == ''}}, Undefined: {{vm.data.input2 == undefined}}) </div> </form>
The code in the ng-required attribute is an AngularJS expression that needs to evaluate to either true, or false. By setting the expression so that each field checks the other field, we satisfy the requirement that if one field is populated, the other must be as well.
Using functions in ng-required
As well as placing an entire expression in the ng-required attribute, you can change it up and call a function in your controller. For example, here's some snippets excerpted that show centralising the code for the ng-required inside the controller for your directive.
The changes to the template/HTML:
Field 1: <input name="input1" ng-model="vm.data.input1" ng-required="vm.data.requiredCheck()"> <br /> Field 2: <input name="input2" ng-model="vm.data.input2" ng-required="vm.data.requiredCheck()">
The code inside the controller:
this.data.requiredCheck = function() { var field1Set = this.input1 != '' && this.input1 != ""; var field2Set = this.input2 != '' && this.input2 != ""; if ((!field1Set && !field2Set) || (field1Set && field2Set)) { return false; } else { return true; } }
This can be extended to do anything you want, really, giving more flexibility than just placing an expression inside the ng-required attribute.