validation con - jQuery Validate:requiere al menos un campo en un grupo para ser llenado




pattern login (9)

Estoy usando el excelente jQuery Validate Plugin para validar algunos formularios. En un formulario, debo asegurarme de que el usuario complete al menos uno de un grupo de campos. Creo que tengo una solución bastante buena y quería compartirla. Por favor, sugiera cualquier mejora que pueda pensar.

Al no encontrar una forma integrada de hacerlo, busqué y encontré el método de validación personalizado de Rebecca Murphey , que fue muy útil.

Lo mejoré de tres maneras:

  1. Para permitirle pasar un selector para el grupo de campos
  2. Para permitirle especificar cuántos de ese grupo se deben completar para que la validación pase
  3. Mostrar todas las entradas en el grupo como pasar la validación tan pronto como una de ellas pase la validación. (Ver gritar a Nick Craver al final.)

Entonces, puede decir "al menos X entradas que coinciden con el selector Y deben llenarse".

El resultado final, con un marcado como este:

<input class="productinfo" name="partnumber">
<input class="productinfo" name="description">

... es un grupo de reglas como esta:

// Both these inputs input will validate if 
// at least 1 input with class 'productinfo' is filled
partnumber: {
   require_from_group: [1,".productinfo"]
  }
description: {
   require_from_group: [1,".productinfo"]
}

El ítem n. ° 3 asume que está agregando una clase de .checked a sus mensajes de error luego de una validación exitosa. Puede hacer esto de la siguiente manera, como se demuestra aquí .

success: function(label) {  
        label.html(" ").addClass("checked"); 
}

Como en la demostración vinculada anteriormente, uso CSS para dar a cada span.error una imagen X como fondo, a menos que tenga la clase .checked , en cuyo caso se obtiene una imagen de marca de verificación.

Aquí está mi código hasta ahora:

jQuery.validator.addMethod("require_from_group", function(value, element, options) {
    var numberRequired = options[0];
    var selector = options[1];
    //Look for our selector within the parent form
    var validOrNot = $(selector, element.form).filter(function() {
         // Each field is kept if it has a value
         return $(this).val();
         // Set to true if there are enough, else to false
      }).length >= numberRequired;

    // The elegent part - this element needs to check the others that match the
    // selector, but we don't want to set off a feedback loop where each element
    // has to check each other element. It would be like:
    // Element 1: "I might be valid if you're valid. Are you?"
    // Element 2: "Let's see. I might be valid if YOU'RE valid. Are you?"
    // Element 1: "Let's see. I might be valid if YOU'RE valid. Are you?"
    // ...etc, until we get a "too much recursion" error.
    //
    // So instead we
    //  1) Flag all matching elements as 'currently being validated'
    //  using jQuery's .data()
    //  2) Re-run validation on each of them. Since the others are now
    //     flagged as being in the process, they will skip this section,
    //     and therefore won't turn around and validate everything else
    //  3) Once that's done, we remove the 'currently being validated' flag
    //     from all the elements
    if(!$(element).data('being_validated')) {
    var fields = $(selector, element.form);
    fields.data('being_validated', true);
    // .valid() means "validate using all applicable rules" (which 
    // includes this one)
    fields.valid();
    fields.data('being_validated', false);
    }
    return validOrNot;
    // {0} below is the 0th item in the options field
    }, jQuery.format("Please fill out at least {0} of these fields."));

¡Hurra!

Gritar

Ahora, para ese reconocimiento: originalmente, mi código ocultaba a ciegas los mensajes de error en los otros campos coincidentes en lugar de volver a validarlos, lo que significaba que si había otro problema (como 'solo se permiten números y se ingresaron letras') , se escondió hasta que el usuario intentó enviar. Esto fue porque no sabía cómo evitar el ciclo de retroalimentación mencionado en los comentarios anteriores. Sabía que debía haber una manera, así que hice una pregunta , y Nick Craver me iluminó. Gracias, Nick!

Pregunta resuelta

Esto fue originalmente un tipo de pregunta "déjame compartir esto y ver si alguien puede sugerir mejoras". Si bien aún me gustaría recibir comentarios, creo que es bastante completo en este momento. (Podría ser más corto, pero quiero que sea fácil de leer y no necesariamente conciso). ¡Así que solo disfruta!

Actualización: ahora parte de la Validación jQuery

Esto se agregó oficialmente a jQuery Validation el 3/04/2012.


Answers

Esa es una excelente solución Nathan. Muchas gracias.

Esta es una forma de hacer que el código anterior funcione, en caso de que alguien tenga problemas para integrarlo, como lo hice yo:

Código dentro del archivo additional-methods.js :

jQuery.validator.addMethod("require_from_group", function(value, element, options) {
...// Nathan's code without any changes
}, jQuery.format("Please fill out at least {0} of these fields."));

// "filone" is the class we will use for the input elements at this example
jQuery.validator.addClassRules("fillone", {
    require_from_group: [1,".fillone"]
});

Código dentro del archivo html :

<input id="field1" class="fillone" type="text" value="" name="field1" />
<input id="field2" class="fillone" type="text" value="" name="field2" />
<input id="field3" class="fillone" type="text" value="" name="field3" />
<input id="field4" class="fillone" type="text" value="" name="field4" />

¡No olvide incluir el archivo additional-methods.js!


Comenzar un nombre de variable con $ es requerido en PHP, pero bastante extraño (en mi humilde opinión) en Javascript. Además, creo que lo mencionas como "$ module" dos veces y "module" una vez, ¿no? Parece que este código no debería funcionar.

Además, no estoy seguro si es la sintaxis normal del plugin jQuery, pero podría agregar comentarios sobre su llamada addMethod, explicando lo que logra. Incluso con la descripción de texto anterior, es difícil seguir el código, porque no estoy familiarizado con el conjunto de campos: relleno, valor, elemento o selector. Quizás la mayor parte de esto es obvio para alguien familiarizado con el complemento Validate, por lo tanto, use el juicio acerca de cuál es la cantidad correcta de explicación.

Tal vez puedas dividir algunos vars para auto-documentar el código; me gusta,

var atLeastOneFilled = module.find(...).length > 0;
if (atLeastOneFilled) {
  var stillMarkedWithErrors = module.find(...).next(...).not(...);
  stillMarkedWithErrors.text("").addClass(...)

(¡Suponiendo que entendiera el significado de estos fragmentos de tu código! :))

No estoy exactamente seguro de lo que significa "módulo", en realidad, ¿hay un nombre más específico que podría darle a esta variable?

Buen código, en general!


Gracias Sean. Eso solucionó el problema que tenía con el código ignorando otras reglas.

También hice algunos cambios para que el mensaje "Por favor complete al menos 1 campo ..." se muestre en un div por separado en lugar de después de todos los campos.

poner en forma validar script

showErrors: function(errorMap, errorList){
            $("#form_error").html("Please fill out at least 1 field before submitting.");
            this.defaultShowErrors();
        },

agregar esto en algún lugar de la página

<div class="error" id="form_error"></div>

añadir a la función addMethod del método require_from_group

 if(validOrNot){
    $("#form_error").hide();
}else{
    $("#form_error").show();
}
......
}, jQuery.format(" &nbsp;(!)"));

Como el formulario en el que estoy trabajando tiene varias regiones clonadas con entradas agrupadas como estas, pasé un argumento adicional al constructor require_from_group, cambiando exactamente una línea de su función addMethod:

var commonParent = $(element).parents(options[2]);

y de esta manera, un selector, ID o nombre de elemento se puede pasar una vez:

jQuery.validator.addClassRules("reqgrp", {require_from_group: [1, ".reqgrp", 'fieldset']});

y el validador restringirá la validación a los elementos con esa clase solo dentro de cada conjunto de campos, en lugar de intentar contar todos los elementos clasificados .reqgrp en el formulario.


Estaba teniendo problemas con otras reglas que no se verificaron junto con esto, así que cambié:

fields.valid();

A esto:

var validator = this;
fields.each(function(){
   validator.valid(this);
});

También hice algunas mejoras (personales), y esta es la versión que estoy usando:

jQuery.validator.addMethod("require_from_group", function(value, element, options){
    var numberRequired = options[0],
    selector = options[1],
    $fields = $(selector, element.form),
    validOrNot = $fields.filter(function() {
        return $(this).val();
    }).length >= numberRequired,
    validator = this;
    if(!$(element).data('being_validated')) {
        $fields.data('being_validated', true).each(function(){
            validator.valid(this);
        }).data('being_validated', false);
    }
    return validOrNot;
}, jQuery.format("Please fill out at least {0} of these fields."));

Buena solución. Sin embargo, tuve el problema de que otras reglas requeridas no funcionaban. Ejecutar .valid () contra el formulario solucionó este problema para mí.

if(!$(element).data('being_validated')) {
  var fields = $(selector, element.form);
  fields.data('being_validated', true); 
  $(element.form).valid();
  fields.data('being_validated', false);
}

Gracias, Nathan. Me salvaste un montón de tiempo.

Sin embargo, debo notar que esta regla no es jQuery.noConflict () lista. Entonces, uno debe reemplazar todos $ con jQuery para trabajar con, digamos, var $j = jQuery.noConflict()

Y tengo una pregunta: ¿cómo haré que se comporte como una regla incorporada? Por ejemplo, si ingreso un correo electrónico, el mensaje "Por favor ingrese un correo electrónico válido" desaparece automáticamente, pero si llevo uno de los campos, el mensaje de error permanece.


He enviado un patch que no sufre los problemas que presenta la versión actual (por lo que la opción "requerida" deja de funcionar correctamente en otros campos, una discusión de los problemas con la versión actual está en github .

Ejemplo en http://jsfiddle.net/f887W/10/

jQuery.validator.addMethod("require_from_group", function (value, element, options) {
var validator = this;
var minRequired = options[0];
var selector = options[1];
var validOrNot = jQuery(selector, element.form).filter(function () {
    return validator.elementValue(this);
}).length >= minRequired;

// remove all events in namespace require_from_group
jQuery(selector, element.form).off('.require_from_group');

//add the required events to trigger revalidation if setting is enabled in the validator
if (this.settings.onkeyup) {
    jQuery(selector, element.form).on({
        'keyup.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

if (this.settings.onfocusin) {
    jQuery(selector, element.form).on({
        'focusin.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

if (this.settings.click) {
    jQuery(selector, element.form).on({
        'click.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

if (this.settings.onfocusout) {
    jQuery(selector, element.form).on({
        'focusout.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

return validOrNot;
}, jQuery.format("Please fill at least {0} of these fields."));

La forma correcta de cambiar el texto del botón si fue "abotonado" usando JQuery es usar el método .button ():

$( ".selector" ).button( "option", "label", "new text" );






jquery validation jquery-plugins jquery-validate