How to override a form with multiple dependent fields?

I am new to Angular. I have a form where the user needs to assign port numbers to 9 different port input fields (context: this is the form for configuring the server environment). The verification requirement means that the port number cannot be assigned twice, so each of the 9 port numbers must be unique.

For this, I have a special verification directive called "srb-unique-port", which I assign to my input fields.

Directive

(function () { 'use strict'; angular .module('account') .directive('srbUniquePort', [srbUniquePort]); function srbUniquePort() { return { restrict: 'A', require: 'ngModel', scope: true, link: function (scope, element, attrs, ngModel) { ngModel.$validators.srbUniquePort = function (val) { if (val == null || val == undefined || val == "" || val==0) return true; var fieldName = attrs.name; var configuration = scope.$eval(attrs.srbUniquePort); var portFieldsToCheck = [ "myRestServicePort", "myRestServicePortSSL", "alfrescoPortHttp", "alfrescoPortHttps", "alfrescoPortTomcatShutdown", "alfrescoPortAJP", "alfrescoPortMySql", "alfrescoPortJOD", "alfrescoPortVti" ]; for (var i = 0; i < portFieldsToCheck.length; i++) { if (fieldName!=portFieldsToCheck[i] && configuration[portFieldsToCheck[i]] == val) { return false; } } return true; } } } } })(); 

HTML form (excerpt just showing 2 of 9 fields):

  ... <md-input-container> <label for="company" translate>COMPANY.CONFIGURATION.DBLIB_WEB_SRVC_PORT</label> <input ng-model="vm.configuration.dblibWebSrvcPort" name="dblibWebSrvcPort" srb-unique-port="vm.configuration"> <div ng-messages="configurationForm.dblibWebSrvcPort.$error"> <div ng-message when="srbUniquePort"> <span translate>COMPANY.CONFIGURATION.VALIDATION.PORT_NOT_UNIQUE</span> </div> </div> </md-input-container> <md-input-container> <label for="company" translate>COMPANY.CONFIGURATION.DBLIB_WEB_SRVC_PORT_SSL</label> <input ng-model="vm.configuration.dblibWebSrvcPortSLL" name="dblibWebSrvcPortSLL" srb-unique-port="vm.configuration"> <div ng-messages="configurationForm.dblibWebSrvcPortSLL.$error"> <div ng-message when="srbUniquePort"> <span translate>COMPANY.CONFIGURATION.VALIDATION.PORT_NOT_UNIQUE</span> </div> </div> </md-input-container> ... 

It basically works for the field that I enter the value in. But the problem is that when I change the value of one input field, I need to check all the other fields again. But I'm not sure that the best way is not to run an infinite loop here, since all fields have an assigned "srb-unique-port".

I already looked at StackOverflow and found this very similar question:

Angular scope directive. $ watch to force check other fields

with this plunker code example: http://plnkr.co/edit/YnxDDAUCS2K7KyXT1AXP?p=preview

but the example given here is different: it concerns only the password and password retry field, where only one field has a validation directive assigned. Therefore, it is different from my case.

I tried to add this to my code:

 scope.$watch(ngModel, function (newValue, oldValue) { ngModel.$validate(); }); 

but this causes endless loops (why does ngModel often change here without any further action than checking, which should always lead to the same thing?).

+1
source share
1 answer

This is the solution I came across. It looks a bit hacked for me, but it works.

 (function () { 'use strict'; angular .module('account') .directive('srbUniquePort', [srbUniquePort]); function srbUniquePort() { return { restrict: 'A', require: 'ngModel', scope: true, link: function (scope, element, attrs, ngModel) { function hasAValue(field) { return !!field; } ngModel.$validators.srbUniquePort = function (val) { var fieldName = attrs.name; var configuration = scope.$eval(attrs.srbUniquePort); var portFieldsToCheck = [ "dblibWebSrvcPort", "dblibWebSrvcPortSLL", "myRestServicePort", "myRestServicePortSSL", "alfrescoPortHttp", "alfrescoPortHttps", "alfrescoPortTomcatShutdown", "alfrescoPortAJP", "alfrescoPortMySql", "alfrescoPortJOD", "alfrescoPortVti" ]; configuration[fieldName] = val; if (scope.$parent.configuration == undefined) { scope.$parent.configuration = JSON.parse(JSON.stringify(configuration)); } scope.$parent.configuration[fieldName] = val; // compare each port field with each other and in case if equality, // remember it by putting a "false" into the validityMap helper variable var validityMap = []; for (var i = 0; i < portFieldsToCheck.length; i++) { for (var j = 0; j < portFieldsToCheck.length; j++) { if (portFieldsToCheck[i] != portFieldsToCheck[j]) { var iFieldHasAValue = hasAValue(scope.$parent.configuration[portFieldsToCheck[i]]); var jFieldHasAValue = hasAValue(scope.$parent.configuration[portFieldsToCheck[j]]); var valHasAValue = hasAValue(val); if (iFieldHasAValue && jFieldHasAValue && scope.$parent.configuration[portFieldsToCheck[i]] == scope.$parent.configuration[portFieldsToCheck[j]] ) { validityMap[portFieldsToCheck[i]] = false; validityMap[portFieldsToCheck[j]] = false; } } } } // in the end, loop through all port fields and set // the validity here manually for (var i = 0; i < portFieldsToCheck.length; i++) { var valid = validityMap[portFieldsToCheck[i]]; if (valid == undefined) valid = true; ngModel.$$parentForm[portFieldsToCheck[i]].$setValidity("srbUniquePort", valid); } // ending with the standard validation for the current field for (var i = 0; i < portFieldsToCheck.length; i++) { if (fieldName != portFieldsToCheck[i] && configuration[portFieldsToCheck[i]] == val) { return false; } } return true; } } } } })(); 
0
source

All Articles