javascript - Cómo validar entradas creadas dinámicamente usando ng-repeat, ng-show (angular)
Tengo una tabla que se crea usando ng-repeat. Quiero agregar validación a cada elemento de la tabla. El problema es que cada celda de entrada tiene el mismo nombre que la celda de arriba y la de abajo. Intenté usar el valor {{$index}} para nombrar las entradas, pero a pesar de que los literales de cadena en HTML parecen correctos, no está funcionando.
Aquí está mi código hasta ahora:
<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>
He intentado quitar el {{}} del índice, pero eso tampoco funciona. Hasta ahora, la propiedad de validación del input está funcionando correctamente, pero el mensaje de error no se muestra.
¿Alguien tiene alguna sugerencia?
Editar: Además de las excelentes respuestas a continuación, aquí hay un artículo de blog que cubre este tema con más detalle: http://www.thebhwgroup.com/blog/2014/08/angularjs-html-form-design-part-2/
Respuesta #1
Desde que se hizo la pregunta, el equipo de Angular ha resuelto este problema haciendo posible crear nombres de entrada de forma dinámica.
Con Angular versión 1.3 y posteriores ahora puedes hacer esto:
<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>
Demostración
Angular 1.3 también introdujo ngMessages, una herramienta más potente para la validación de formularios. Puedes usar la misma técnica con 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>
Respuesta #2
AngularJS depende de los nombres de los inputs para mostrar los errores de validación.
Desafortunadamente, a día de hoy, no es posible (sin usar una directiva personalizada) generar dinámicamente un nombre de un input. De hecho, al revisar la documentación del input podemos ver que el atributo name acepta únicamente una cadena de texto.
Para resolver el problema de 'nombre dinámico' necesitas crear un formulario interno (ver 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>
La otra alternativa sería escribir una directiva personalizada para esto.
Aquí está el jsFiddle que muestra el uso del ngForm: http://jsfiddle.net/pkozlowski_opensource/XK2ZT/2/
Respuesta #3
Si no quieres usar ng-form, puedes usar una directiva personalizada que cambiará el atributo name del formulario. Coloca esta directiva como un atributo en el mismo elemento que tu ng-model.
Si estás utilizando otras directivas en conjunto, ten cuidado de que no tengan la propiedad "terminal" establecida, de lo contrario esta función no podrá ejecutarse (dado que tiene una prioridad de -1).
Por ejemplo, al usar esta directiva con ng-options, debes ejecutar esta corrección de una línea: 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;
}
};
});
A menudo encuentro útil usar ng-init para asignar el $index a un nombre de variable. Por ejemplo:
<fieldset class='inputs' ng-repeat="question questions" ng-init="qIndex = $index">
Esto cambia tu expresión regular a:
name = name.replace(/\{\{qIndex\}\}/g, scope.qIndex);
Si tienes múltiples ng-repeats anidados, ahora puedes usar estos nombres de variables en lugar de $parent.$index.
Definición de "terminal" y "priority" para directivas: https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object
Comentario en Github sobre la necesidad de un parche temporal para ng-option: https://github.com/angular/angular.js/commit/9ee2cdff44e7d496774b340de816344126c457b3#commitcomment-6832095 https://twitter.com/aljohri/status/482963541520314369
ACTUALIZACIÓN :
También puedes hacer que esto funcione con 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;
}
}
}
}
};
});
Respuesta #4
Utilice la directiva ng-form dentro de la etiqueta en la que está utilizando la directiva ng-repeat. Luego puede usar el ámbito creado por la directiva ng-form para referenciar un nombre genérico. Por ejemplo:
<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>
Crédito a: http://www.benlesh.com/2013/03/angular-js-validating-form-elements-in.html
