Uso de JQuery Validate Plugin para validar múltiples campos de formulario con nombres idénticos

Tengo una forma generada dinámicamente con campos de entrada con el mismo nombre (por ejemplo: “mapa”). No tengo la opción de cambiar los nombres de campo o generar nombres de campo únicos porque el código del controlador de formulario (Perl / CGI) está diseñado para manejar una matriz de valores de entrada (en este caso @map ).

¿Cómo puedo usar el complemento JQuery Validate para validar un formulario en dicha situación? Específicamente, quisiera que exactamente un elemento de la matriz enviada tenga un cierto valor fijo. Actualmente estoy usando un controlador de eventos personalizado que crea un objeto JSON con serializeArray() y luego lo cruza para garantizar que se cumpla la condición. Pero como utilicé Validate Plugin en el rest de la aplicación, me preguntaba si ese caso podría manejarse usando el mismo complemento aquí también.

Gracias por su atención.

Pasé un tiempo buscando e intentando cosas diferentes cuando finalmente probé la manera más trivial de hacer validaciones en múltiples campos. Cada campo y sus clones comparten una clase única para cada conjunto. Acabo de pasar las entradas con esa clase y agregué mis reglas de validación como de costumbre. Espero que esto pueda ayudar a alguien más.

  $("#submit").click(function(){ $("input.years").each(function(){ $(this).rules("add", { required: true, messages: { required: "Specify the years you worked" } } ); }); $("input.employerName").each(function(){ $(this).rules("add", { required: true, messages: { required: "Specify the employer name" } } ); }); $("input.employerPhone").each(function(){ $(this).rules("add", { required: true, minlength: 10, messages: { required: "Specify the employer phone number", minlength: "Not long enough" } } ); }); $("input.position").each(function(){ $(this).rules("add", { required: true, messages: { required: "Specify your position" } } ); }); $("input.referenceName").each(function(){ $(this).rules("add", { required: true, messages: { required: "Specify the reference name" } } ); }); $("input.referencePhone").each(function(){ $(this).rules("add", { required: true, minlength: 10, messages: { required: "Specify your reference phone number", minlength: "Not long enough" } } ); }); // Now do your normal validation here, but don't assign rules/messages for the fields we just set them for }); 

Viejo hilo lo sé, pero lo encontré en busca de la solución al mismo problema.

Se ha publicado una solución más elegante aquí: http://web-funda.blogspot.com/2009/05/jquery-validation-for-array-of-input.html

Simplemente edite jquery.validate.js y cambie el checkForm a

  checkForm: function() { this.prepareForm(); for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) { if (this.findByName( elements[i].name ).length != undefined && this.findByName( elements[i].name ).length > 1) { for (var cnt = 0; cnt < this.findByName( elements[i].name ).length; cnt++) { this.check( this.findByName( elements[i].name )[cnt] ); } } else { this.check( elements[i] ); } } return this.valid(); } 

Como no puedo comentar la respuesta de @scampbell, no sé si se trata de puntos de reputación o porque el hilo acaba de cerrarse, tengo una contribución a su respuesta,

En lugar de cambiar el archivo de origen jquery.validation, puede simplemente anular la función que necesita editar solo en las páginas que lo requieren.

un ejemplo sería:

 $.validator.prototype.checkForm = function() { //overriden in a specific page this.prepareForm(); for (var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++) { if (this.findByName(elements[i].name).length !== undefined && this.findByName(elements[i].name).length > 1) { for (var cnt = 0; cnt < this.findByName(elements[i].name).length; cnt++) { this.check(this.findByName(elements[i].name)[cnt]); } } else { this.check(elements[i]); } } return this.valid(); }; 

esta podría no ser la mejor solución, pero al menos evita editar los archivos fuente que podrían ser reemplazados más adelante cuando se lance una nueva versión. donde su función anulada podría o no romperse

Acabo de aprender de un correo electrónico del autor de Plugins, Jörn Zaefferer, que la validación requiere que los nombres de los campos sean únicos, excepto los botones de opción y las casillas de verificación.

La respuesta de Jason hará el truco, pero no quería agregar eventos de clic adicionales en cada formulario en el que hice esto.

En mi caso, tengo el complemento de validación, considero los nombres que terminan con ‘[]’ diferentes a pesar de que pueden tener nombres de campo idénticos. Para hacer esto, sobreescribí estos dos métodos internos después de las cargas de jquery.validate.js.

 $.validator.prototype.elements= function() { var validator = this, rulesCache = {}; // select all valid inputs inside the form (no submit or reset buttons) // workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved return $([]).add(this.currentForm.elements) .filter(":input") .not(":submit, :reset, :image, [disabled]") .not( this.settings.ignore ) .filter(function() { !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this); // select only the first element for each name (EXCEPT elements that end in []), and only those with rules specified if ( (!this.name.match(/\[\]/gi) && this.name in rulesCache) || !validator.objectLength($(this).rules()) ) return false; rulesCache[this.name] = true; return true; }); }; $.validator.prototype.idOrName = function(element) { // Special edit to get fields that end with [], since there are several [] we want to disambiguate them // Make an id on the fly if the element doesnt have one if(element.name.match(/\[\]/gi)) { if(element.id){ return element.id; } else { var unique_id = new Date().getTime(); element.id = new Date().getTime(); return element.id; } } return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name); }; 

Simplemente use un atributo no utilizado de la entrada para almacenar el nombre original, luego simplemente cambie el nombre con su índice adjunto:

 function addMultiInputNamingRules(form, field, rules){ $(form).find(field).each(function(index){ $(this).attr('alt', $(this).attr('name')); $(this).attr('name', $(this).attr('name')+'-'+index); $(this).rules('add', rules); }); 

}

 function removeMultiInputNamingRules(form, field){ $(form).find(field).each(function(index){ $(this).attr('name', $(this).attr('alt')); $(this).removeAttr('alt'); }); 

}

Luego, después de configurar su validador:

 addMultiInputNamingRules('#form-id', 'input[name="multifield[]"]', { required:true }); 

y cuando hayas terminado de validar, regresa de la siguiente manera:

 removeMultiInputNamingRules('#form-id', 'input[alt="multifield[]"]'); 

— ¡Espero que esto ayude!

Así es como lo hice. Un poco más fácil que los métodos propuestos anteriormente:

 function validateTab(tab) { var valid = true; $(tab).find('input').each(function (index, elem) { var isElemValid = $("#registrationForm").validate().element(elem); if (isElemValid != null) { //this covers elements that have no validation rule valid = valid & isElemValid; } }); return valid; } 

En mi caso, tengo un asistente (de 3 pasos) que resultó ser aún más complejo ya que no quiero validar todos los campos a la vez. Básicamente coloco componentes en tabs y si la primera pestaña es válida, paso a la siguiente, hasta que llego a la última, luego de lo cual envío todos los datos. Por lo tanto, el parámetro tab es el elemento tab real (que es un div ). Luego recorro todos los elementos de entrada de los niños a mi pestaña y los controlo para ver si son válidos.

Todo lo demás es estándar.


Para completar, aquí está el rest del código: cómo se realiza el envío del formulario y cómo se ve mi validador:

  

Y aquí la función js llama:

 function moveToNextTab(currentTab) { var tabs = document.getElementsByClassName("tab"); //loop through tabs and validate the current one. //If valid, hide current tab and make next one visible. } 

Estoy usando estas reglas de validación (que creo en JQuery.ready):

 $("#registrationForm").validate({ rules: { birthdate: { required: true, date: true }, name: "required", surname: "required", address: "required", postalCode: "required", city: "required", country: "required", email: { required: true, email: true } } }); 

Estoy usando “jQuery validation plug-in 1.7”.

El problema por el que varios elementos “$ (: input)” que comparten el mismo nombre no están validados

es el método $ .validator.element:

 elements: function() { var validator = this, rulesCache = {}; // select all valid inputs inside the form (no submit or reset buttons) // workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved return $([]).add(this.currentForm.elements) .filter(":input") .not(":submit, :reset, :image, [disabled]") .not( this.settings.ignore ) .filter(function() { !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this); // select only the first element for each name, and only those with rules specified if ( this.name in rulesCache || !validator.objectLength($(this).rules()) ) return false; rulesCache[this.name] = true; return true; }); }, 

La condición

if (this.name en rulesCache || …..

evalúa para el segundo y los siguientes elementos que comparten el mismo nombre verdadero ….

La solución sería tener la condición:

(this.id || this.name) en rulesCache

Disculpe, JS puritans, que (this.id || this.name) no está al 100% …

Por supuesto, el

rulesCache [this.name] = true;

la línea debe ser cambiada apropiadamente también.

Entonces el método $ .validator.prototype.elements sería:

 $(function () { if ($.validator) { //fix: when several input elements shares the same name, but has different id-ies.... $.validator.prototype.elements = function () { var validator = this, rulesCache = {}; // select all valid inputs inside the form (no submit or reset buttons) // workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved return $([]).add(this.currentForm.elements) .filter(":input") .not(":submit, :reset, :image, [disabled]") .not(this.settings.ignore) .filter(function () { var elementIdentification = this.id || this.name; !elementIdentification && validator.settings.debug && window.console && console.error("%o has no id nor name assigned", this); // select only the first element for each name, and only those with rules specified if (elementIdentification in rulesCache || !validator.objectLength($(this).rules())) return false; rulesCache[elementIdentification] = true; return true; }); }; } 

});

Tal vez me esté faltando el punto, pero como el validador no funciona con varios nombres (intenté … ¡fallado!) Cambié mi formulario para cambiar dinámicamente los nombres, establecí las reglas y luego desarmé los nombres en el envío.

Dos métodos (ignore las cosas de wlog, solo se envía a la consola):

 // convert the field names into generated ones to allow fields with the same names // to be validated individually. The original names are stored as data against the // elements, ready to be replaced. The name is replaced with // "multivalidate--", eg original => 'multivalidate-original-1' function setGeneratedNamesWithValidationRules(form, fields, rules) { var length = fields.length; for (var i=0; i < length; ++i ){ var name = fields[i]; var idCounter = 0; // we match either the already converted generator names or the original $("form [name^='multivalidate-" + name + "'], form [name='" + name + "']").each(function() { // identify the real name, either from the stored value, or the actual name attribute var realName = $(this).data('realName'); if (realName == undefined) { realName = $(this).attr("name"); $(this).data('realName', realName); } wlog("Name: " + realName + " (actual: " + $(this).attr("name") + "), val: " + $(this).val() + ". Rules: " + rules[realName]); $(this).attr("name", "multivalidate-" + realName + "-" + idCounter); if (rules[realName]) { $(this).rules("add", rules[realName]); } idCounter++; }); } } function revertGeneratedNames(form, fields) { var length = fields.length; for (var i=0; i < length; ++i ){ var name = fields[i]; wlog("look for fields names [" + name + "]"); $("form [name^='multivalidate-" + name + "']").each(function() { var realName = $(this).data('realName'); if (realName == undefined) { wlog("Error: field named [" + $(this).attr("name") + "] does not have a stored real name"); } else { wlog("Convert [" + $(this).attr("name") + "] back to [" + realName + "]"); $(this).attr("name", realName); } }); } } 

En la carga de formulario, y cada vez que agrego dinámicamente otra fila, llamo al método set, por ejemplo

 setGeneratedNamesWithValidationRules($("#my-dynamic-form"), ['amounts'], { 'amounts': 'required'} ); 

Esto cambia los nombres para permitir la validación individual.

En el submitHandler: thingumy después de la validación llamo revertir, es decir,

 revertGeneratedNames(form, ['amounts']); 

Que cambia los nombres a los originales antes de publicar los datos.

Creo que malinterpretaste el funcionamiento de los formularios HTML. Cada elemento de formulario debe tener un nombre único, excepto varias casillas de verificación y botones que le permiten elegir una o varias opciones para un campo de datos.

En su caso, no solo fallaría la validación de JQuery, sino también un validador de formulario del lado del servidor, porque no puede asignar las entradas a los campos de datos. Supongamos que desea que el usuario ingrese pre nombre, apellido, dirección de correo electrónico, fax (opcional) y todos sus campos de entrada tengan name="map"

Entonces recibirías estas listas al enviar:

 map = ['Joe','Doe','joe.doeAThotmail.com','++22 20182238'] //All fields completed map = ['Joe','Doe','joe.doeAThotmail.com'] //OK, all mandatory fields completed map = ['Doe', 'joe.doeAThotmail.com','++22 20182238']//user forgot prename, should yield error 

Verá que es imposible validar esta forma de manera confiable.

Recomiendo volver a visitar la documentación de su controlador de formulario de Perl o adaptarlo si lo escribió por su cuenta.

Para mí, esto se resolvió muy fácilmente deshabilitando la depuración

  $("#_form").validate({ debug:false, //debug: true, ... }); 

Existe una solución simple:

 $(document).ready(function() { $(".form").each(function() { $(this).validate({ ... ,errorContainer: $(".status_mess",this) // use "this" as reference to that instance of form. ... }); }); });