如何validation使用ng-repeatdynamic创build的input,ng-show(angular)

我有一个使用ng-repeat创build的表格。 我想要validation表中的每个元素。 问题是每个input单元格的名称都与它上面和下面的单元格相同。 我试图使用{{$index}}值来命名input,但是尽pipeHTML中的string文字显示正确,但它现在正在工作。

这是我现在的代码:

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

我试图从索引中删除{{}} ,但是这也不起作用。 截至目前,input的validation属性工作正常,但不显示错误消息。

任何人有任何build议?

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

AngularJS依靠input名称来暴露validation错误。

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

要解决“dynamic名称”问题, 您需要创build一个内部表单(请参阅ng表单 )

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

另一种select是为此编写一个自定义指令。

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

由于问题被问到,Angular团队通过dynamic创buildinput名称来解决这个问题。

使用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,这是一个更强大的表单validation工具。 你可以用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> 

如果你不想使用ng-form,你可以使用自定义指令来改变表单的name属性。 把这个指令作为一个属性放在与你的ng模型相同的元素上。

如果您正在使用其他指令,请注意它们没有设置“terminal”属性,否则此函数将无法运行(因为它的优先级为-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设置为variables名是很有用的。 例如:

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

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

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

如果你有多个嵌套的ng-repeat,你现在可以使用这些variables名而不是$ parent。$ index。

定义指令的“terminal”和“priority”: 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/482963541520314369

更新:

你也可以用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; } } } } }; }); 

在使用ng-repeat指令的标签内部使用ng-form指令。 然后可以使用由ng-form指令创build的作用域来引用通用名称。 例如:

  <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

在控制器http://jsfiddle.net/82PX4/3/上添加了一个更复杂的例子“自定义validation”;

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

看看这些解决scheme,Al Johri提供的解决scheme是最接近我的需求的,但是他的指令有点不太可编程,我想。 这是他的解决scheme的我的版本:

 angular.module("app", []) .directive("dynamicFormName", function() { return { restrict: "A", priority: 0, require: ["form"], compile: function() { return { pre: function preLink(scope, iElement, iAttrs, ctrls) { var name = "field" + scope.$index; if (iAttrs.dnfnNameExpression) { name = scope.$eval(iAttrs.dnfnNameExpression); } var parentForm = iElement.parent().controller("form"); if (parentForm) { var formCtrl = ctrls[0]; delete parentForm[formCtrl.$name]; formCtrl.$name = name; parentForm[formCtrl.$name] = formCtrl; } } } } }; }); 

这个解决scheme可以让你传递一个名称生成器expression式到指令,并避免locking他正在使用的模式replace。

我最初使用这个解决scheme也遇到了麻烦,因为它没有显示在标记中使用它的例子,所以这里是我如何使用它。

 <form name="theForm"> <div ng-repeat="field in fields"> <input type="number" ng-form name="theInput{{field.id}}" ng-model="field.value" dynamic-form-name dnfn-name-expression="'theInput' + field.id"> </div> </form> 

我有一个更完整的工作在github上的例子。

validation与ng重复使用,如果我使用以下语法scope.step3Form['item[107][quantity]'].$touched我不知道这是一个最佳的做法或最好的解决scheme,但它的工作原理

 <tr ng-repeat="item in items"> <td> <div class="form-group"> <input type="text" ng-model="item.quantity" name="item[<% item.id%>][quantity]" required="" class="form-control" placeholder = "# of Units" /> <span ng-show="step3Form.$submitted || step3Form['item[<% item.id %>][quantity]'].$touched"> <span class="help-block" ng-show="step3Form['item[<% item.id %>][quantity]'].$error.required"> # of Units is required.</span> </span> </div> </td> </tr> 

这太晚了,但可能会帮助任何人

  1. 为每个控件创build唯一的名称
  2. 通过使用fromname[uniquname].$error

示例代码:

 <input ng-model="r.QTY" class="span1" name="QTY{{$index}}" ng-pattern="/^[\d]*\.?[\d]*$/" required/> <div ng-messages="formName['QTY' +$index].$error" ng-show="formName['QTY' +$index].$dirty || formName.$submitted"> <div ng-message="required" class='error'>Required</div> <div ng-message="pattern" class='error'>Invalid Pattern</div> </div> 

在这里看到工作演示

这是可能的,这里是我如何做一个input表同样的事情。

用这样的forms包装表格

那就用这个吧

我有一个带有多重嵌套指令的表单,它们都包含input,select等等。这些元素都包含在ng-repeats和dynamicstring值中。

这是如何使用该指令:

 <form name="myFormName"> <nested directives of many levels> <your table here> <perhaps a td here> ex: <input ng-repeat=(index, variable) in variables" type="text" my-name="{{ variable.name + '/' + 'myFormName' }}" ng-model="variable.name" required /> ex: <select ng-model="variable.name" ng-options="label in label in {{ variable.options }}" my-name="{{ variable.name + index + '/' + 'myFormName' }}" </select> </form> 

注意:如果你需要序列化一个input表,你可以添加和索引string连接。 这是我所做的。

 app.directive('myName', function(){ var myNameError = "myName directive error: " return { restrict:'A', // Declares an Attributes Directive. require: 'ngModel', // ngModelController. link: function( scope, elem, attrs, ngModel ){ if( !ngModel ){ return } // no ngModel exists for this element // check myName input for proper formatting ex. something/something checkInputFormat(attrs); var inputName = attrs.myName.match('^\\w+').pop(); // match upto '/' assignInputNameToInputModel(inputName, ngModel); var formName = attrs.myName.match('\\w+$').pop(); // match after '/' findForm(formName, ngModel, scope); } // end link } // end return function checkInputFormat(attrs){ if( !/\w\/\w/.test(attrs.rsName )){ throw myNameError + "Formatting should be \"inputName/formName\" but is " + attrs.rsName } } function assignInputNameToInputModel(inputName, ngModel){ ngModel.$name = inputName } function addInputNameToForm(formName, ngModel, scope){ scope[formName][ngModel.$name] = ngModel; return } function findForm(formName, ngModel, scope){ if( !scope ){ // ran out of scope before finding scope[formName] throw myNameError + "<Form> element named " + formName + " could not be found." } if( formName in scope){ // found scope[formName] addInputNameToForm(formName, ngModel, scope) return } findForm(formName, ngModel, scope.$parent) // recursively search through $parent scopes } }); 

这应该处理很多情况,你只是不知道表单将在哪里。 或者,也许你有嵌套的forms,但出于某种原因,你想附加这个input名称为两个表格? 那么,只需传入您想要附加input名称的表单名称即可。

我想要的是一种将dynamic值分配给input的方法,我永远不会知道,然后调用$ scope.myFormName。$ valid。

你可以添加任何你想要的东西:更多的表格更多的表单input,嵌套的forms,任何你想要的。 只需传递您想要validationinput的表单名称即可。 然后在表单上提交询问$ scope.yourFormName。$ valid

这将在ng-repeat中得到名称,在表单validation中单独出现。

 <td> <input ng-model="r.QTY" class="span1" name="{{'QTY' + $index}}" ng-pattern="/^[\d]*\.?[\d]*$/" required/> </td> 

但是我在查找validation消息时遇到了麻烦,所以我不得不使用ng-init来将它parsing为对象键。

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

build立在pkozlowski.opensource的答案 ,我已经添加了一个方法来使dynamicinput名称也与ngMessages工作。 注意ng-form元素上的ng-init部分和furryName的使用。 furryName变成包含input name属性的variables值的variablesname

 <ion-item ng-repeat="animal in creatures track by $index"> <ng-form name="animalsForm" ng-init="furryName = 'furry' + $index"> <!-- animal is furry toggle buttons --> <input id="furryRadio{{$index}}" type="radio" name="{{furryName}}" ng-model="animal.isFurry" ng-value="radioBoolValues.boolTrue" required > <label for="furryRadio{{$index}}">Furry</label> <input id="hairlessRadio{{$index}}" name="{{furryName}}" type="radio" ng-model="animal.isFurry" ng-value="radioBoolValues.boolFalse" required > <label for="hairlessRadio{{$index}}">Hairless</label> <div ng-messages="animalsForm[furryName].$error" class="form-errors" ng-show="animalsForm[furryName].$invalid && sectionForm.$submitted"> <div ng-messages-include="client/views/partials/form-errors.ng.html"></div> </div> </ng-form> </ion-item> 

这里是我如何做的一个例子,我不知道这是不是最好的解决scheme,但完美的作品。

首先,在HTML中的代码。 看看ng类,它调用hasError函数。 也看input的名称声明。 我使用$索引来创build不同的input名称。

 <div data-ng-repeat="tipo in currentObject.Tipo" ng-class="{'has-error': hasError(planForm, 'TipoM', 'required', $index) || hasError(planForm, 'TipoM', 'maxlength', $index)}"> <input ng-model="tipo.Nombre" maxlength="100" required name="{{'TipoM' + $index}}"/> 

而现在,这里是hasError函数:

 $scope.hasError = function (form, elementName, errorType, index) { if (form == undefined || elementName == undefined || errorType == undefined || index == undefined) return false; var element = form[elementName + index]; return (element != null && element.$error[errorType] && element.$touched); }; 

我的要求与原始问题有所不同,但希望我可以帮助正在经历同样问题的人。

我必须根据范围variables来定义是否需要字段。所以我基本上必须设置ng-required="myScopeVariable" (这是一个布尔variables)。

 <div class="align-left" ng-repeat="schema in schemas"> <input type="text" ng-required="schema.Required" /> </div> 

如果你使用ng-repeat $索引就像这样工作

  name="QTY{{$index}}" 

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

我们必须以ng模式显示ng-show

  <span class="alert-error" ng-show="form['QTY' + $index].$error.pattern"> <span class="alert-error" ng-show="form['QTY' + $index].$error.required">