Enable/disable 使用 `ng-form` 验证带有嵌套子表单的 angular 表单
Enable/disable validation for angular form with nested subforms using `ng-form`
我需要 enable/disable 基于范围变量 $scope.isValidationRequired
ng-form="myForm"
下的 Angular 表单或子表单中的所有验证规则。因此,如果 isValidationRequired
为 false
,则指定字段组的验证集 none 将为 运行,结果将始终为 myForm.$valid==true
,否则, 验证规则将 运行 照常。
我做了很多研究,发现 Angular 没有开箱即用的功能。但是,我发现了一些附加组件或进行了一些自定义,这是可能的。
例如,我可以将附加组件 angular-conditional-validation (github and demo) 与自定义指令 enable-validation="isValidationRequired"
一起使用。这将是完美的,除了我无法将此功能应用于 ng-form
下的一组字段。我必须为适用的每个字段添加此指令。
另一种解决方案是使用 Angular $validators
管道进行自定义验证。这需要一些额外的努力,我没有时间,因为冲刺快结束了,我必须在几天内给出一些结果。
如果您有任何其他建议,请post回答。
用例:
为了阐明这一点的必要性,我将提及用例。最终用户可以用无效数据填写表单,他可以单击 Save
按钮,在这种情况下,不应触发验证规则。仅当用户单击 Validate and Save
时才应触发验证规则。
解决方案:
查看最终的 plunker 代码 here。
更新:根据下面的评论,如果在 ng-form
下使用内部子表单,该解决方案将导致浏览器挂起。需要更多的努力来调试和解决这个问题。如果只使用一级,那么它就可以正常工作。
更新:plunker here 更新了一个更通用的解决方案。现在代码将使用在 ng-form
下具有子表单的表单。函数 setAllInputsDirty()
检查对象是否为 $$parentForm
以停止递归。此外,changeValidity()
将检查对象是否为使用 $addControl
的表单,然后它将调用自身来验证其子对象。到目前为止,此功能运行良好,但需要一些额外的优化。
一个想法是在禁用验证标志的情况下重置摘要循环中的错误。您可以遍历更改时的表单错误,并将它们一一设置为有效。
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function() {
if ($scope.isValidationRequired === "false") {
for (var error in $scope.form.$error) {
while ($scope.form.$error[error]) {
$scope.form.$error[error][0].$setValidity(error, true);
}
}
}
}
这是一个 plunkr:https://plnkr.co/edit/fH4vGVPa1MwljPFknYHZ
这是防止无限循环和无限递归的更新答案。此外,代码依赖于一个已知的根形式,可以对其进行一些调整以使其更通用。
参考文献:Pixelastic blog and
笨蛋:https://plnkr.co/edit/ycPmYDSg6da10KdoNCiM?p=preview
更新:代码改进使其适用于每个子表单中每个字段的多个错误,并循环以确保在子表单级别清除错误
var app = angular.module('plunker', []);
app.controller('MainCtrl', ["$scope", function($scope) {
$scope.isValidationRequired = true;
var rootForm = "form";
function setAllInputsDirty(scope) {
angular.forEach(scope, function(value, key) {
// We skip non-form and non-inputs
if (!value || value.$dirty === undefined) {
return;
}
// Recursively applying same method on all forms included in the form except the parent form
if (value.$addControl && key !== "$$parentForm") {
return setAllInputsDirty(value);
}
if (value.$validate){
value.$validate();
}
// Setting inputs to $dirty, but re-applying its content in itself
if (value.$setViewValue) {
//debugger;
return value.$setViewValue(value.$viewValue);
}
});
}
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function(theForm) {
debugger;
//This will check if validation is truned off, it will
// clear all validation errors
if (!theForm) {
theForm = $scope[rootForm];
}
if ($scope.isValidationRequired === "false") {
for (var error in theForm.$error) {
errTypeArr = theForm.$error[error];
angular.forEach (errTypeArr, function(value, idx) {
var theObjName = value.$name;
var theObj = value;
if (theObj.$addControl) {
//This is a subform, so call the function recursively for each of the children
var isValid=false;
while (!isValid) {
$scope.changeValidity(theObj);
isValid = theObj.$valid;
}
} else {
while (theObj.$error[error]) {
theObj.$setValidity(error, true);
}
}
})
}
} else {
setAllInputsDirty($scope);
}
}
}]);
我需要 enable/disable 基于范围变量 $scope.isValidationRequired
ng-form="myForm"
下的 Angular 表单或子表单中的所有验证规则。因此,如果 isValidationRequired
为 false
,则指定字段组的验证集 none 将为 运行,结果将始终为 myForm.$valid==true
,否则, 验证规则将 运行 照常。
我做了很多研究,发现 Angular 没有开箱即用的功能。但是,我发现了一些附加组件或进行了一些自定义,这是可能的。
例如,我可以将附加组件 angular-conditional-validation (github and demo) 与自定义指令 enable-validation="isValidationRequired"
一起使用。这将是完美的,除了我无法将此功能应用于 ng-form
下的一组字段。我必须为适用的每个字段添加此指令。
另一种解决方案是使用 Angular $validators
管道进行自定义验证。这需要一些额外的努力,我没有时间,因为冲刺快结束了,我必须在几天内给出一些结果。
如果您有任何其他建议,请post回答。
用例:
为了阐明这一点的必要性,我将提及用例。最终用户可以用无效数据填写表单,他可以单击 Save
按钮,在这种情况下,不应触发验证规则。仅当用户单击 Validate and Save
时才应触发验证规则。
解决方案:
查看最终的 plunker 代码 here。
更新:根据下面的评论,如果在 ng-form
下使用内部子表单,该解决方案将导致浏览器挂起。需要更多的努力来调试和解决这个问题。如果只使用一级,那么它就可以正常工作。
更新:plunker here 更新了一个更通用的解决方案。现在代码将使用在 ng-form
下具有子表单的表单。函数 setAllInputsDirty()
检查对象是否为 $$parentForm
以停止递归。此外,changeValidity()
将检查对象是否为使用 $addControl
的表单,然后它将调用自身来验证其子对象。到目前为止,此功能运行良好,但需要一些额外的优化。
一个想法是在禁用验证标志的情况下重置摘要循环中的错误。您可以遍历更改时的表单错误,并将它们一一设置为有效。
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function() {
if ($scope.isValidationRequired === "false") {
for (var error in $scope.form.$error) {
while ($scope.form.$error[error]) {
$scope.form.$error[error][0].$setValidity(error, true);
}
}
}
}
这是一个 plunkr:https://plnkr.co/edit/fH4vGVPa1MwljPFknYHZ
这是防止无限循环和无限递归的更新答案。此外,代码依赖于一个已知的根形式,可以对其进行一些调整以使其更通用。
参考文献:Pixelastic blog and
笨蛋:https://plnkr.co/edit/ycPmYDSg6da10KdoNCiM?p=preview
更新:代码改进使其适用于每个子表单中每个字段的多个错误,并循环以确保在子表单级别清除错误
var app = angular.module('plunker', []);
app.controller('MainCtrl', ["$scope", function($scope) {
$scope.isValidationRequired = true;
var rootForm = "form";
function setAllInputsDirty(scope) {
angular.forEach(scope, function(value, key) {
// We skip non-form and non-inputs
if (!value || value.$dirty === undefined) {
return;
}
// Recursively applying same method on all forms included in the form except the parent form
if (value.$addControl && key !== "$$parentForm") {
return setAllInputsDirty(value);
}
if (value.$validate){
value.$validate();
}
// Setting inputs to $dirty, but re-applying its content in itself
if (value.$setViewValue) {
//debugger;
return value.$setViewValue(value.$viewValue);
}
});
}
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function(theForm) {
debugger;
//This will check if validation is truned off, it will
// clear all validation errors
if (!theForm) {
theForm = $scope[rootForm];
}
if ($scope.isValidationRequired === "false") {
for (var error in theForm.$error) {
errTypeArr = theForm.$error[error];
angular.forEach (errTypeArr, function(value, idx) {
var theObjName = value.$name;
var theObj = value;
if (theObj.$addControl) {
//This is a subform, so call the function recursively for each of the children
var isValid=false;
while (!isValid) {
$scope.changeValidity(theObj);
isValid = theObj.$valid;
}
} else {
while (theObj.$error[error]) {
theObj.$setValidity(error, true);
}
}
})
}
} else {
setAllInputsDirty($scope);
}
}
}]);