javascript 使用 AngularJS 跳过嵌套表单验证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19333544/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 15:12:35  来源:igfitidea点击:

Skip nested forms validation with AngularJS

javascriptangularjs

提问by Antelle

How can I skip validation of nested forms with AngularJS? I have to make an outer form valid even when its child form is invalid.

如何使用 AngularJS 跳过嵌套表单的验证?即使其子表单无效,我也必须使外部表单有效。

In the example below outer form should be valid (fOuter.$validmust be true). By default, it is not. Is there an option?

在下面的示例中,外部形式应该是有效的(fOuter.$valid必须为真)。默认情况下,它不是。有选择吗?

Code (jsFiddle):

代码(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>

采纳答案by Maxim Shoustin

In Angular forms can be nested. This means that the outer form is valid when all of the child forms are valid as well.

在 Angular 表单中可以嵌套。这意味着当所有子表单也都有效时,外部表单也是有效的。

So there is no way to make outer form to be valid automatically (through $validkey) when one of inner invalid.

因此,$valid当内部形式之一无效时,无法使外部形式自动(通过密钥)有效。

Try to use error.required

尝试使用 error.required

   <h3>Outer form (valid={{!fOuter.txtOuter.$error.required}})</h3>

Demo Fiddle

演示 Fiddle

From Angular ngForm docs:

来自 Angular ngForm 文档

The other way should be to use controller, like:

另一种方法应该是使用控制器,例如:

<h3>Outer form (valid={{isOuterFormValid}})</h3>

controller

控制器

$scope.isOuterFormValid = true;

// here, add listener on each input and change flag `isOuterFormValid`
... 

回答by 91K00

Here is my solution inspired by mbernath, that isolates completely the form itself from its father.

这是我受 mbernath 启发的解决方案,它将表单本身与其父亲完全隔离。

This solution take care of the:

此解决方案负责:

  • Form validity ($valid, $invalid)
  • Form interaction ($pristine, $dirty)
  • Nested forms validity and interaction
  • 表单有效性($valid, $invalid)
  • 表单交互($pristine,$dirty)
  • 嵌套形式有效性和交互

See it in action in this JSFiddle.

在这个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);
        }
    };
}]);

To use it just call the directive "isolate-form" :

要使用它,只需调用指令“isolate-form”:

<form name="parent">
    <input type="text" ng-model="outside"/>
    <ng-form name="subform" isolate-form>
        <input type="text" ng-model="inside"/>
    </ng-form>
</form>

回答by mbernath

I faced the same problem. Inside a larger form I needed to have a subform with several controls that shouldn't touch the state of the parent form.

我遇到了同样的问题。在一个较大的表单中,我需要有一个带有多个控件的子表单,这些控件不应触及父表单的状态。

Here's my solution: I wrote a directive "null-form" that removes the subform from the parent form and that does not send any state changes its parent.

这是我的解决方案:我编写了一个指令“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);
    }
  };
}]);

You can then use it like this:

然后你可以像这样使用它:

<form name="parent">
  <input type="text" ng-model="outside"/>
  <ng-form name="subform" null-form>
    <input type="text" ng-model="inside"/>
  </ng-form>
</form>

Any change or negative validation of "inside" won't take an effect on "parent".

“内部”的任何更改或负面验证都不会对“父”产生影响。

There's one downside, however, due to this solution: subform will not have any state either nor will its CSS classes like ng-invalid etc. work. To accomplish this you would need to re-implement this functionality from the original form-controller.

然而,由于这个解决方案,有一个缺点:子表单不会有任何状态,它的 CSS 类(如 ng-invalid 等)也不会工作。为此,您需要从原始表单控制器重新实现此功能。

回答by Christopher Lenz

At least with Angular 1.5 it seems to be enough to remove the nested form from the parent using $removeControl:

至少在 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);
    }
  };
});

Et voila, pristine and validity states of the parent are no longer affected by the nested form.

瞧,父级的原始状态和有效性状态不再受嵌套形式的影响。

回答by AviG

I found out the solution that worked best was Anton's.

我发现最有效的解决方案是安东的。

Setting the nullFormCtrl suggested by mbernath disables validation on the child form (thxs for paving the way though...).

设置 mbernath 建议的 nullFormCtrl 会禁用对子表单的验证(尽管为此铺平了道路......)。

The only change that I made was in the way the parentForm is accessed. angular does provide a method for that.

我所做的唯一更改是访问 parentForm 的方式。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);
            }
        }
    };
}]);

回答by Sugumar

I am a newbie to Angular however, please check whether the below approach helps.

但是,我是 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>

<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>

回答by Yuriy Rozhovetskiy

I had the same issue and resolve it with bit change in local copy of angular.js file itself.

我遇到了同样的问题,并通过在 angular.js 文件本身的本地副本中进行了一些更改来解决它。

Basically, I added new function to the FormController as below:

基本上,我向 FormController 添加了新函数,如下所示:

form.$resetParent = function() {
    parentForm = nullFormCtrl;
};

and create custom directive:

并创建自定义指令:

angular.module('myApp').directive('dtIsolatedForm', function () {
    return {
        restrict: 'A',
        require: '?form',
        link: function (scope, element, attrs, formController) {
            if (!formController || !formController.$parentForm) {
                return;
            }

            formController.$resetParent();
        }
    };
});

回答by montor

Also inspired by mbernath, I found a simpler solution. It consists of creating a dummy form-like directive only for isolation. The directive stops propagation from nested elements to outer form but it doesn't have any form functionality. You can nest ngForms inside and have them fully functional.

同样受到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;
                }
            };
        })

The way is to specify the name of controller in directive definition (name: 'form'). This property isn't documented, but is used for creating ngForm directive in angular source.

方法是在指令定义(name: 'form')中指定控制器的名称。此属性没有记录,但用于在angular source 中创建 ngForm 指令。

回答by Anton

i'd like to suggest mbernath's version without downside

我想建议 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);
        }
    }
};}]);

回答by gr3g

From your controller :

从您的控制器:

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);
};

FormControlleris an Object that gives you information about your form state.
Adding a form to a form, with ng-formis adding a FormControllerproperty to your original FormControllerObject.

FormController是一个对象,它为您提供有关表单状态的信息。
向表单添加表单,ng-form即向FormController原始FormController对象添加属性。

This has the advantage of not adding a html directive to all your input elements.

这具有不向所有输入元素添加 html 指令的优点。