如何验证使用 ng-repeat、ng-show(角度)动态创建的输入

IT技术 javascript html angularjs validation grid
2021-01-17 07:15:25

我有一个使用 ng-repeat 创建的表。我想为表中的每个元素添加验证。问题是每个输入单元格都与其上方和下方的单元格具有相同的名称。我尝试使用该{{$index}}值来命名输入,但尽管 HTML 中的字符串文字看起来是正确的,但它现在可以正常工作了。

这是我现在的代码:

<tr ng-repeat="r in model.BSM ">
   <td>
      <input ng-model="r.QTY" class="span1" name="QTY{{$index}}" ng-pattern="/^[\d]*\.?[\d]*$/" required/>
      <span class="alert-error" ng-show="form.QTY{{$index}}.$error.pattern"><strong>Requires a number.</strong></span>
      <span class="alert-error" ng-show="form.QTY{{$index}}.$error.required"><strong>*Required</strong></span>
   </td>
</tr>

我试过{{}}从索引中删除,但这也不起作用。截至目前,输入的验证属性工作正常,但未显示错误消息。

有人有什么建议吗?

编辑:除了下面的好答案,这里有一篇博客文章更详细地介绍了这个问题:http : //www.thebhwgroup.com/blog/2014/08/angularjs-html-form-design-part-2 /

6个回答

自从提出这个问题以来,Angular 团队已经通过动态创建输入名称解决了这个问题。

使用Angular 1.3 及更高版本,您现在可以执行以下操作:

<form name="vm.myForm" novalidate>
  <div ng-repeat="p in vm.persons">
    <input type="text" name="person_{{$index}}" ng-model="p" required>
    <span ng-show="vm.myForm['person_' + $index].$invalid">Enter a name</span>
  </div>
</form>

演示

Angular 1.3 还引入了 ngMessages,这是一种更强大的表单验证工具。您可以对 ngMessages 使用相同的技术:

<form name="vm.myFormNgMsg" novalidate>
    <div ng-repeat="p in vm.persons">
      <input type="text" name="person_{{$index}}" ng-model="p" required>
      <span ng-messages="vm.myFormNgMsg['person_' + $index].$error">
        <span ng-message="required">Enter a name</span>
      </span>
    </div>
  </form>
我注意到如果你想让它工作,你的表单名称不能有连字符。有谁知道这是为什么?
2021-03-23 07:15:25
这是完美的,而且比做指令要容易得多——可以将表单传递给组件并使用此方法。谢了哥们!
2021-03-24 07:15:25
@PatrickSzalapski:这是因为 Angular 使用了表单名称,并且带有连字符的变量名称在 Javascript 中是无效的语法。解决方法:<span ng-show="vm['my-form']['person_' + $index].$invalid">输入名称</span>
2021-03-24 07:15:25
您希望将所有错误显示在表单顶部的一处吗?
2021-04-01 07:15:25
我注意到如果你动态删除重复的项目,$valid输入属性会不正确false
2021-04-09 07:15:25

AngularJS 依赖输入名称来暴露验证错误。

不幸的是,截至今天,(不使用自定义指令)动态生成输入的名称是不可能的。事实上,检查输入文档我们可以看到 name 属性只接受一个字符串。

要解决“动态名称”问题,您需要创建一个内部表单(请参阅ng-form

<div ng-repeat="social in formData.socials">
      <ng-form name="urlForm">
            <input type="url" name="socialUrl" ng-model="social.url">
            <span class="alert error" ng-show="urlForm.socialUrl.$error.url">URL error</span>
      </ng-form>
  </div>

另一种选择是为此编写自定义指令。

这是显示 ngForm 用法的 jsFiddle:http : //jsfiddle.net/pkozlowski_opensource/XK2ZT/2/

嵌套表单不被认为是有效的 HTML stackoverflow.com/questions/379610/can-you-nest-html-forms angular 计划是否对此进行了修复?
2021-03-12 07:15:25
那太棒了。但是具有多个同名文本框是否有效 html?
2021-03-15 07:15:25
伟大的。应该注意的是,如果您ng-repeat绑定了,table tr那么您必须使用ng-form="myname"attr。
2021-03-30 07:15:25
@Blowsie 您不是在此处嵌套真实形式,而是在ng-formDOM 元素中嵌套,因此此处与其他 SO 问题的链接无关。
2021-04-06 07:15:25
应该编辑此答案:问题github.com/angular/angular.js/issues/1404自 AngularJS 1.3.0 以来已解决(自 2014 年 9 月提交)
2021-04-10 07:15:25

如果您不想使用 ng-form,您可以使用自定义指令来更改表单的名称属性。将此指令作为属性放在与 ng-model 相同的元素上。

如果您结合使用其他指令,请注意它们没有设置“终端”属性,否则此函数将无法运行(假设它的优先级为 -1)。

例如,当将此指令与 ng-options 一起使用时,您必须运行这一行monkeypatch:https : //github.com/AlJohri/bower-angular/commit/eb17a967b7973eb7fc1124b024aa8b3ca540a155

angular.module('app').directive('fieldNameHack', function() {
    return {
      restrict: 'A',
      priority: -1,
      require: ['ngModel'],
      // the ngModelDirective has a priority of 0.
      // priority is run in reverse order for postLink functions.
      link: function (scope, iElement, iAttrs, ctrls) {

        var name = iElement[0].name;
        name = name.replace(/\{\{\$index\}\}/g, scope.$index);

        var modelCtrl = ctrls[0];
        modelCtrl.$name = name;

      }
    };
});

我经常发现使用 ng-init 将 $index 设置为变量名很有用。例如:

<fieldset class='inputs' ng-repeat="question questions" ng-init="qIndex = $index">

这会将您的正则表达式更改为:

name = name.replace(/\{\{qIndex\}\}/g, scope.qIndex);

如果您有多个嵌套的 ng-repeat,您现在可以使用这些变量名称代替 $parent.$index。

指令的“终端”和“优先级”的定义https : //docs.angularjs.org/api/ng/service/ $compile#directive-definition-object

Github 关于需要 ng-option monkeypatch 的评论:https : //github.com/angular/angular.js/commit/9ee2cdff44e7d496774b340de816344126c457b3#commitcomment-6832095 https://twitter.com/aljohri/status/41436

更新:

您也可以使用 ng-form 进行这项工作。

angular.module('app').directive('formNameHack', function() {
    return {
      restrict: 'A',
      priority: 0,
      require: ['form'],
      compile: function() {
        return {
          pre: function(scope, iElement, iAttrs, ctrls) {
            var parentForm = $(iElement).parent().controller('form');
            if (parentForm) {
                var formCtrl = ctrls[0];
                delete parentForm[formCtrl.$name];
                formCtrl.$name = formCtrl.$name.replace(/\{\{\$index\}\}/g, scope.$index);
                parentForm[formCtrl.$name] = formCtrl;
            }
          }
        }
      }
    };
});
明确地说,未选择此答案并不表示它不是最佳答案。它是在最初提出问题近 2 年后才发布的。如果您遇到同样的问题,除了选定的答案之外,我还会考虑这个答案和 tomGreen 的答案。
2021-03-28 07:15:25

在使用 ng-repeat 指令的标签内使用 ng-form 指令。然后,您可以使用 ng-form 指令创建的范围来引用通用名称。例如:

    <div class="form-group col-sm-6" data-ng-form="subForm" data-ng-repeat="field in justificationInfo.justifications"">

        <label for="{{field.label}}"><h3>{{field.label}}</h3></label>
        <i class="icon-valid" data-ng-show="subForm.input.$dirty && subForm.input.$valid"></i>
        <i class="icon-invalid" data-ng-show="subForm.input.$dirty && subForm.input.$invalid"></i>
        <textarea placeholder="{{field.placeholder}}" class="form-control" id="{{field.label}}" name="input" type="text" rows="3" data-ng-model="field.value" required>{{field.value}}</textarea>

    </div>

归功于:http : //www.benlesh.com/2013/03/angular-js-validating-form-elements-in.html

接受的答案对我不起作用。然而这一次做到了。(我使用 Angular 2.1.14)
2021-03-22 07:15:25
+1 这个答案对我 有用 检查链接:你只需要添加ng-form="formName"到具有 ng-repeat 的标签......它就像一个魅力:)
2021-04-03 07:15:25

在控制器http://jsfiddle.net/82PX4/3/的一侧添加了带有“自定义验证”的更复杂的示例

<div class='line' ng-repeat='line in ranges' ng-form='lineForm'>
    low: <input type='text' 
                name='low'
                ng-pattern='/^\d+$/' 
                ng-change="lowChanged(this, $index)" ng-model='line.low' />
    up: <input type='text' 
                name='up'
                ng-pattern='/^\d+$/'
                ng-change="upChanged(this, $index)" 
                ng-model='line.up' />
    <a href ng-if='!$first' ng-click='removeRange($index)'>Delete</a>
    <div class='error' ng-show='lineForm.$error.pattern'>
        Must be a number.
    </div>
    <div class='error' ng-show='lineForm.$error.range'>
        Low must be less the Up.
    </div>
</div>