使用 AngularJS 跳过嵌套表单验证
Skip nested forms validation with AngularJS
如何使用AngularJS跳过嵌套表单的验证?即使外部表单无效,我也必须使外部表单有效。
在下面的示例中,外部形式应该是有效的(fOuter.$valid
必须为真)。默认情况下,它不是。有没有选择?
代码(jsFiddle):
<div ng-app ng-controller="Ctrl">
<ng-form name="fOuter">
<h3>Outer form (valid={{fOuter.$valid}})</h3>
<input type="text" name="txtOuter" ng-model="outer" placeholder="(required)" required />
<ng-form name="fInner">
<h3>Inner form (valid={{fInner.$valid}})</h3>
<input type="text" name="txtInner" ng-model="inner" placeholder="(required)" required />
</ng-form>
</ng-form>
</div>
这是我受mbernath启发的解决方案,它将形式本身与其父亲完全隔离开来。
此解决方案负责:
- 表单有效期($valid、$invalid)
- 表单交互($pristine、$dirty)
- 嵌套表单的有效性和交互
在此 JSFiddle 中查看它的实际效果。
angular.module('isolateForm',[]).directive('isolateForm', [function () {
return {
restrict: 'A',
require: '?form',
link: function (scope, elm, attrs, ctrl) {
if (!ctrl) {
return;
}
// Do a copy of the controller
var ctrlCopy = {};
angular.copy(ctrl, ctrlCopy);
// Get the parent of the form
var parent = elm.parent().controller('form');
// Remove parent link to the controller
parent.$removeControl(ctrl);
// Replace form controller with a "isolated form"
var isolatedFormCtrl = {
$setValidity: function (validationToken, isValid, control) {
ctrlCopy.$setValidity(validationToken, isValid, control);
parent.$setValidity(validationToken, true, ctrl);
},
$setDirty: function () {
elm.removeClass('ng-pristine').addClass('ng-dirty');
ctrl.$dirty = true;
ctrl.$pristine = false;
},
};
angular.extend(ctrl, isolatedFormCtrl);
}
};
}]);
要使用它,只需调用指令"隔离形式":
<form name="parent">
<input type="text" ng-model="outside"/>
<ng-form name="subform" isolate-form>
<input type="text" ng-model="inside"/>
</ng-form>
</form>
我遇到了同样的问题。在一个更大的窗体中,我需要有一个包含多个控件的子窗体,这些控件不应触及父窗体的状态。
这是我的解决方案:我写了一个指令"null-form",它从父表单中删除子表单,并且不发送任何状态更改其父表单。
angular.module('nullForm',[]).directive('nullForm', [function () {
return {
restrict: 'A',
require: '?form',
link: function link(scope, element, iAttrs, formController) {
if (! formController) {
return;
}
// Remove this form from parent controller
var parentFormController = element.parent().controller('form');
parentFormController.$removeControl(formController);
// Replace form controller with a "null-controller"
var nullFormCtrl = {
$addControl: angular.noop,
$removeControl: angular.noop,
$setValidity: angular.noop,
$setDirty: angular.noop,
$setPristine: angular.noop
};
angular.extend(formController, nullFormCtrl);
}
};
}]);
然后,您可以像这样使用它:
<form name="parent">
<input type="text" ng-model="outside"/>
<ng-form name="subform" null-form>
<input type="text" ng-model="inside"/>
</ng-form>
</form>
对"内部"的任何更改或否定验证都不会对"父级"产生影响。
然而,由于这个解决方案,有一个缺点:子表单也不会有任何状态,它的CSS类(如ng-validing等)也不会工作。为此,您需要从原始表单控制器重新实现此功能。
至少在 Angular 1.5 中,使用 $removeControl
从父级中删除嵌套表单似乎就足够了:
module.directive('isolateForm', function() {
return {
restrict: 'A',
require: '?form',
link: function(scope, element, attrs, formController) {
if (!formController) {
return;
}
var parentForm = formController.$$parentForm; // Note this uses private API
if (!parentForm) {
return;
}
// Remove this form from parent controller
parentForm.$removeControl(formController);
}
};
});
瞧,父级的原始状态和有效性状态不再受嵌套形式的影响。
我发现最有效的解决方案是安东的解决方案。
设置 mbernath 建议的 nullFormCtrl 会禁用对子窗体的验证(不过,这要为铺平道路......
我所做的唯一更改是访问父窗体的方式。 Angular 确实为此提供了一种方法。
.directive('isolateForm', [function () {
return {
restrict: 'A',
require: '?form',
link: function link(scope, element, iAttrs, formController) {
if (!formController) {
return;
}
// Remove this form from parent controller
formController.$$parentForm.$removeControl(formController)
var _handler = formController.$setValidity;
formController.$setValidity = function (validationErrorKey, isValid, cntrl) {
_handler(validationErrorKey, isValid, cntrl);
formController.$$parentForm.$setValidity(validationErrorKey, true, this);
}
}
};
}]);
在角度形式可以嵌套。这意味着当所有子窗体也有效时,外部窗体也有效。
因此,当内部形式之一无效时,没有办法使外部形式自动有效(通过$valid
键)。
尝试使用error.required
<h3>Outer form (valid={{!fOuter.txtOuter.$error.required}})</h3>
演示小提琴
来自 Angular ngForm 文档:
另一种方式应该是使用控制器,例如:
<h3>Outer form (valid={{isOuterFormValid}})</h3>
控制器
$scope.isOuterFormValid = true;
// here, add listener on each input and change flag `isOuterFormValid`
...
我是 Angular 的新手,但是请检查以下方法是否有帮助。
<div ng-app ng-controller="Ctrl">
<ng-form name="fOuter">
<h3>Outer form (valid={{fOuter.$valid}})</h3>
<ng-form name="fInner1">
<h3>Inner form 1 (valid={{fInner1.$valid}})</h3>
<input type="text" name="txtInner1" ng-model="outer" placeholder="(required)" required />
</ng-form>
<ng-form name="fInner2">
<h3>Inner form 2 (valid={{fInner2.$valid}})</h3>
<input type="text" name="txtInner2" ng-model="inner" placeholder="(required)" required />
</ng-form>
</ng-form>
</div>
我遇到了同样的问题,并通过对 angular.js 文件本身的本地副本进行位更改来解决它。
基本上,我向表单控制器添加了新功能,如下所示:
form.$resetParent = function() {
parentForm = nullFormCtrl;
};
并创建自定义指令:
angular.module('myApp').directive('dtIsolatedForm', function () {
return {
restrict: 'A',
require: '?form',
link: function (scope, element, attrs, formController) {
if (!formController || !formController.$parentForm) {
return;
}
formController.$resetParent();
}
};
});
同样受到mbernath的启发,我找到了一个更简单的解决方案。它包括创建一个仅用于隔离的虚拟表单类指令。该指令停止从嵌套元素到外部表单的传播,但它没有任何表单功能。您可以将ngForms嵌套在内部并使它们完全正常运行。
angular.directive('formIsolator', function () {
return {
name: 'form',
restrict: 'EAC',
controller: function() {
this.$addControl = angular.noop;
this.$$renameControl = function(control, name) {
control.$name = name;
};
this.$removeControl = angular.noop;
this.$setValidity = angular.noop;
this.$setDirty = angular.noop;
this.$setPristine = angular.noop;
this.$setSubmitted = angular.noop;
}
};
})
方法是在指令定义(name: 'form'
)中指定控制器的名称。此属性未记录,但用于在角度源中创建 ngForm 指令。
我想建议Mbernath的版本没有缺点
angular.module('yourModule').directive('isolatedForm', [function () {
return {
restrict: 'A',
require: '?form',
link: function link(scope, element, iAttrs, formController) {
if (!formController) return;
// Remove this form from parent controller
var parentFormController = element.parent().controller('form');
parentFormController.$removeControl(formController);
// override default behavior
var _handler = formController.$setValidity;
formController.$setValidity = function (validationErrorKey, isValid, cntrl) {
_handler(validationErrorKey, isValid, cntrl);
parentFormController.$setValidity(validationErrorKey, true, this);
}
}
};}]);
从控制器:
Ctrl.isOuterFormValid = function() {
var outerFormIsValid = true;
for(var prop in Ctrl.formName) {
//The form is only inValid if the property is not a new form and it is invalid
if(pvCtrl.pvForm[prop].constructor.name !== "FormController" &&
pvCtrl.pvForm[prop].$invalid){
outerFormIsValid = false;
}
}
alert(outerFormIsValid);
};
FormController 是一个对象,它为您提供有关表单状态的信息。
使用ng-form
将窗体添加到窗体就是将FormController
属性添加到原始FormController
对象。
这样做的好处是不会向所有输入元素添加 html 指令。
基本上,目标是分离嵌套表单之间的连接,并独立执行自己的验证/访问表单的$error
对象。这可以通过在两个嵌套表单之间引入模型控制器并允许该模型控制器确定父表单控制器和子表单控制器何时应有效/无效来实现。这可以通过增加$setValidity()
来实现,它决定了表单何时应该有效/无效。
请在下面的 plunker 链接中找到我的代码。我在父窗体和子窗体之间引入了一个模型控制器。在这里,我抽象了$error
父窗体中的子窗体的对象。这意味着,父表单将无法看到子表单的问题,但是当子表单中的某些字段无效时,它将失效。只有中间模型控制器知道子窗体中哪些字段有问题。可以调整此逻辑或根据我们的需求进行扩展。如果有人需要更多代码方面的澄清,请告诉我。
[plnkr]: https://plnkr.co/edit/5gvctSSqmWiEAUE3YUcZ?p=preview
在检查表单是否有效之前,只需删除嵌套表单即可! vm.parentForm.$removeControl(vm.nestledForm);
- 解析javascript表单验证器
- Jquery表单验证插件-如果选中复选框,如何在提交时执行某些操作
- 自定义表单验证和提交
- Javascript表单验证的缺点
- Jquery的表单验证不起作用
- 语义ui表单验证:图像url
- jQuery表单验证适用于Mozilla和Internet Explorer,但不适用于Chrome或Safari
- JavaScript表单验证-“;“错误”;不按需要工作
- Mailchimp内联表单验证不适用于JS加载的HTML
- jQuery/JS获胜't在表单验证后重定向
- jquery表单验证复选框错误显示
- javascript表单验证-数值和密码
- 我想知道如何在javascript中进行表单验证
- jQuery表单验证(如果==“执行此操作”)仅适用于第一次输入
- 使用记录保存的消息进行表单验证
- 表单验证工作不正常,在不检查条目的情况下继续
- 如何对未知表单元素进行表单验证
- 使用ajax、php和mysql进行表单验证
- JS表单验证未发生
- 表单验证jsphp路径