如何重新验证具有多个依赖字段的表单?
How to revalidate a form with multiple dependent fields?
我是 Angular 的新手。我有一个表单,用户需要将端口号分配给 9 个不同的端口输入字段(上下文:这是一个用于服务器环境配置的表单)。 验证要求是任何端口号都不能被分配两次,所以9个端口号中的每一个都需要是唯一的。
为此,我有一个名为 "srb-unique-port" 的 自定义验证指令 ,我将其分配给我的输入字段。
指令:
(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 表单(摘录,仅显示 9 个字段中的 2 个):
...
<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>
...
它基本上适用于我当前输入值的字段。但问题是,当我更改一个输入字段的值时,我还需要重新验证所有其他依赖字段。但我不确定最好的方法是什么,以免 运行 陷入无限循环,因为所有字段都分配了 "srb-unique-port"。
我已经查看了 Whosebug 并发现了这个非常相似的问题:
Angular directive with scope.$watch to force validation of other fields
使用这个 plunker 示例代码:
http://plnkr.co/edit/YnxDDAUCS2K7KyXT1AXP?p=preview
但此处提供的示例有所不同:它仅涉及一个密码和一个密码重复字段,其中只有一个字段分配了验证指令。
所以这和我的情况不同。
我试图在上面的代码中添加:
scope.$watch(ngModel, function (newValue, oldValue) {
ngModel.$validate();
});
但这会导致无限循环(为什么 ngModel 经常在此处更改而除了验证之外没有任何进一步的操作,结果应该始终相同?)。
这是我最终得到的解决方案。对我来说看起来有点黑,但它有效。
(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;
}
}
}
}
})();
我是 Angular 的新手。我有一个表单,用户需要将端口号分配给 9 个不同的端口输入字段(上下文:这是一个用于服务器环境配置的表单)。 验证要求是任何端口号都不能被分配两次,所以9个端口号中的每一个都需要是唯一的。
为此,我有一个名为 "srb-unique-port" 的 自定义验证指令 ,我将其分配给我的输入字段。
指令:
(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 表单(摘录,仅显示 9 个字段中的 2 个):
...
<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>
...
它基本上适用于我当前输入值的字段。但问题是,当我更改一个输入字段的值时,我还需要重新验证所有其他依赖字段。但我不确定最好的方法是什么,以免 运行 陷入无限循环,因为所有字段都分配了 "srb-unique-port"。
我已经查看了 Whosebug 并发现了这个非常相似的问题:
Angular directive with scope.$watch to force validation of other fields
使用这个 plunker 示例代码: http://plnkr.co/edit/YnxDDAUCS2K7KyXT1AXP?p=preview
但此处提供的示例有所不同:它仅涉及一个密码和一个密码重复字段,其中只有一个字段分配了验证指令。 所以这和我的情况不同。
我试图在上面的代码中添加:
scope.$watch(ngModel, function (newValue, oldValue) {
ngModel.$validate();
});
但这会导致无限循环(为什么 ngModel 经常在此处更改而除了验证之外没有任何进一步的操作,结果应该始终相同?)。
这是我最终得到的解决方案。对我来说看起来有点黑,但它有效。
(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;
}
}
}
}
})();