如何验证使用 ng-repeat、ng-show(角度)动态创建的输入
How to validate inputs dynamically created using ng-repeat, ng-show (angular)
>我有一个使用 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/
自从提出这个问题以来,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>
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/
如果您不想使用 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设置为变量名很有用。例如:
<fieldset class='inputs' ng-repeat="question questions" ng-init="qIndex = $index">
这会将您的正则表达式更改为:
name = name.replace(/'{'{qIndex'}'}/g, scope.qIndex);
如果您有多个嵌套的 ng 重复,您现在可以使用这些变量名称而不是 $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-6832095https://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 指令创建的作用域来引用泛型名称。例如:
<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/一侧使用"自定义验证">
<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>
查看这些解决方案,上面 Al Johri 提供的解决方案最接近我的需求,但他的指令比我想要的要少一些可编程。以下是我对他的解决方案的版本:
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;
}
}
}
}
};
});
此解决方案允许您将名称生成器表达式传递给指令,并避免锁定到他使用的模式替换。
我最初在这个解决方案上也遇到了麻烦,因为它没有显示在标记中使用它的示例,所以这是我如何使用它的方式。
<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上有一个更完整的工作示例。
使用以下语法scope.step3Form['item[107][quantity]'].$touched
,验证正在使用ng repeat。我不知道这是最佳实践还是最佳解决方案,但它有效
<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>
基于 pkozlowski.opensource 的答案,我添加了一种让动态输入名称也适用于 ngMessage 的方法。请注意ng-form
元素上的ng-init
部分和furryName
的使用。 furryName
成为包含input
name
属性的变量值的变量名称。
<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>
这是我如何做到这一点的一个例子,我不知道它是否是最好的解决方案,但效果很好。
首先,HTML代码。看看ng-class,它调用的是hasError函数。另请查看输入的名称声明。我使用 $index 创建不同的输入名称。
<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);
};
为时已晚,但可能可以帮助任何人
- 为每个控件创建唯一的名称
- 使用
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>
在此处查看工作演示
如果你使用 ng-repeat$index 像这样工作
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">
这是可能的,这是我如何使用输入表做同样的事情。
以这样的形式包装表格
然后就用这个
我有一个带有多嵌套指令的表单,这些指令都包含输入、选择等......这些元素都包含在 ng 重复和动态字符串值中。
这是如何使用该指令:
<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>
注意:如果您需要序列化输入表,则可以向字符串连接添加和索引;这就是我所做的。
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
}
});
这应该可以处理许多您不知道表单将在哪里的情况。 或者,也许您有嵌套表单,但由于某种原因您想将此输入名称附加到两个表单上? 好吧,只需传入要附加输入名称的表单名称即可。
我想要的是一种为我永远不会知道的输入分配动态值的方法,然后调用$scope.myFormName.$valid。
您可以添加任何其他您想要的内容:更多表格,更多表单输入,嵌套表单,任何您想要的。 只需传递要验证输入的表单名称即可。 然后在表单提交时询问 $scope.yourFormName.$valid
这将使 ng-repeat 中的名称在表单验证中单独出现。
<td>
<input ng-model="r.QTY" class="span1" name="{{'QTY' + $index}}" ng-pattern="/^['d]*'.?['d]*$/" required/>
</td>
但是我无法让它在其验证消息中查找,所以我不得不使用 ng-init 让它将变量解析为对象键。
<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>
我的要求与原始问题的要求略有不同,但希望我可以帮助遇到与我相同问题的人。
我必须根据范围变量定义字段是否是必需的。所以我基本上必须设置ng-required="myScopeVariable"
(这是一个布尔变量(。
<div class="align-left" ng-repeat="schema in schemas">
<input type="text" ng-required="schema.Required" />
</div>
- 以角度管理动态URL
- 在有角度的ui网格中设置动态列的问题
- 无法在有角度的谷歌地图中动态传递地图样式
- 角度媒体播放器和动态内容
- 在动态加载的html中使用角度控制器
- 我们可以加载动态角度对话框吗
- 动态创建角度视图
- 如何验证使用 ng-repeat、ng-show(角度)动态创建的输入
- 角度 JS - 显示/隐藏动态表的一些行
- 如何使用角度两个绑定来动态创建的剑道网格
- 如何将活动类添加到第一个动态选项卡而不是角度的静态选项卡
- 角度 JS 动态选项卡字段
- 动态模板 URL 在角度 ui 路由器中解析时
- 设置多个观察程序以触发动态事件的角度替代解决方案 - 优化的观察者模式
- 如何使用角度JS制作动态内容可编辑表
- 根据角度 js 应用程序中的条件动态添加类
- 基于数组中元素的角度集动态类
- 设置控制器动态角度
- 如何在角度 2 中向动态加载的子组件提供父组件的模型
- 动态角度模板