33

如何使用 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>
4

12 回答 12

38

这是我受 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);
        }
    };
}]);

要使用它,只需调用指令“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>
于 2014-07-24T14:18:15.457 回答
19

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

这是我的解决方案:我编写了一个指令“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-invalid 等)也不会工作。为此,您需要从原始表单控制器重新实现此功能。

于 2014-05-08T11:57:02.377 回答
13

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

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

于 2016-05-27T11:04:10.047 回答
2

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

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

我所做的唯一更改是访问 parentForm 的方式。角度确实为此提供了一种方法。

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

于 2015-07-23T12:41:51.043 回答
0

我想推荐 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);
        }
    }
};}]);
于 2015-03-26T12:38:58.373 回答
0

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

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

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();
        }
    };
});
于 2014-07-25T16:16:38.103 回答
0

同样受到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')中指定控制器的名称。此属性未记录在案,但用于在angular source中创建 ngForm 指令。

于 2015-02-18T20:57:14.207 回答
0

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

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

尝试使用error.required

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

演示Fiddle

来自 Angular ngForm 文档

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

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

控制器

$scope.isOuterFormValid = true;

// here, add listener on each input and change flag `isOuterFormValid`
... 
于 2013-10-12T11:23:16.390 回答
0

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

于 2014-03-07T07:49:38.357 回答
0

基本上,目标是分离嵌套表单之间的连接并独立执行您自己的验证/访问$error表单的对象。这可以通过在两个嵌套表单之间引入 modelController 并允许 modelController 确定父表单控制器和子表单控制器何时应该有效/无效来实现。这可以通过增加 来实现$setValidity(),它确定表单何时应该有效/无效。

请在下面的 plunker 链接中找到我的代码。我在父窗体和子窗体之间引入了一个模型控制器。这里我$error 从父表单中抽象出子表单的对象。意思是,父表单将无法看到子表单有什么问题,但是当子表单中的某些字段无效时,它将失效。只有中间的 modelController 知道子表单中哪些字段有问题。这个逻辑可以根据我们的需要进行调整或扩展。如果有人在代码方面需要更多说明,请告诉我。

[plnkr]: https://plnkr.co/edit/5gvctSSqmWiEAUE3YUcZ?p=preview
于 2017-11-12T19:46:51.713 回答
0

从您的控制器:

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添加属性。FormControllerFormController

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

于 2016-11-21T15:30:19.843 回答
0

在检查表单是否有效之前,只需删除嵌套的表单! vm.parentForm.$removeControl(vm.nestledForm);

于 2020-05-15T16:02:34.430 回答