如何验证输入动态创建使用 ng- 重复,ng- 显示(角度)

我有一个使用 n- 重复创建的表。我想为表中的每个元素添加验证。问题是,每个输入单元格的名称与上面和下面的单元格相同。我尝试使用 {{$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>

我已经尝试从索引中删除 {{}},但这也不起作用。到目前为止,输入的验证属性正常工作,但是没有显示错误消息。

有人有什么建议吗?

编辑: 除了下面的绝佳答案,这里还有一篇博客文章更详细地介绍了这个问题: < a href = “ http://www.thebhwgroup.com/blog/2014/08/angularjs-html-form-design-part-2/”rel = “ norefrer”> http://www.thebhwgroup.com/blog/2014/08/angularjs-html-form-design-part-2/

161869 次浏览

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

不幸的是,到目前为止,(如果不使用自定义指令)不可能动态地生成输入的名称。事实上,检查 输入文件我们可以看到 name 属性只接受字符串。

为了解决“动态名称”问题 您需要创建一个内部表单(参见 < a href = “ http://docs.angularjs.org/api/ng.direct: ngForm”rel = “ norefrer”> 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>

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

下面是 jsFiddle,它显示了 ngForm: http://jsfiddle.net/pkozlowski_opensource/XK2ZT/2/的用法

在控制器 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>

在使用 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

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

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

例如,当对 ng-options 使用此指令时,必须运行以下一行 monkeyypatch: 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);

如果您有多个嵌套的 n- 重复项,那么现在可以使用这些变量名,而不是 $Parent.$index。

指令的“终端”和“优先级”的定义: https://docs.angularjs.org/api/ng/service/元编译 # 指令-定义-对象

Github 关于需要 n- 选项 monkeypatch 的评论: Https://github.com/angular/angular.js/commit/9ee2cdff44e7d496774b340de816344126c457b3#commitcomment-6832095 Https://twitter.com/aljohri/status/482963541520314369

更新:

您还可以使用 n- 形式来实现这一点。

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

看看这些解决方案,以上由 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的更完整的工作示例。

这是可能的,这里是我如何做同样的事情与输入表。

把桌子包成这样的形状

那就用这个

我有一个包含多个嵌套指令的表单,所有这些指令都包含 input (s)、 select (s)等等。 这些元素都包含在 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。$有效。

您可以添加任何其他您想要的东西: 更多的表更多的表单输入,嵌套的表单,任何您想要的东西。只需传递要验证输入的表单名称。然后在表单上提交询问 $scope.yourFormName。$有效

这将使 ng- 重复中的名称在表单验证中单独出现。

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

但是我很难让它在验证消息中查找,所以我必须使用 n- 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>

如果我使用以下语法 scope.step3Form['item[107][quantity]'].$touched,验证将使用 ngrepeat 我不知道这是最佳实践还是最佳解决方案,但它确实有效

<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 的 回答的基础上,我添加了一种使动态输入名称也能与 消息一起工作的方法。请注意 ng-form元素上的 ng-init部分和 furryName的使用。furryName成为包含 inputname属性的变量值的变量名。

<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 编写代码。 看 n 类,它正在调用 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);
};

自从这个问题被提出以来,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,这是一个更强大的表单验证工具:

<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-required="myScopeVariable"(这是一个布尔变量)。

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

现在已经太晚了,但也许它能帮助任何人

  1. 为每个控件创建唯一的名称
  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>

参见工作 演示在这里

如果您使用 n- 重复 $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>

我们必须用 n-pattern 来表示 n-show

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