jQuery Validate: richiede che almeno un campo in un gruppo sia riempito


98

Sto usando l'eccellente plugin jQuery Validate per convalidare alcuni moduli. In un modulo, devo assicurarmi che l'utente compili almeno uno di un gruppo di campi. Penso di avere una buona soluzione e volevo condividerla. Si prega di suggerire eventuali miglioramenti che si possono pensare.

Non trovando un modo integrato per farlo, ho cercato e trovato il metodo di convalida personalizzato di Rebecca Murphey , che è stato molto utile.

L'ho migliorato in tre modi:

  1. Per farti passare un selettore per il gruppo di campi
  2. Per consentirti di specificare quanti componenti di quel gruppo devono essere riempiti affinché la convalida passi
  3. Per mostrare tutti gli input nel gruppo come convalida in corso non appena uno di essi supera la convalida. (Vedi il grido a Nick Craver alla fine.)

Quindi puoi dire "almeno X input che corrispondono al selettore Y devono essere riempiti".

Il risultato finale, con un markup come questo:

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

... è un gruppo di regole come questo:

// 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"]
}

L'articolo 3 presuppone che tu stia aggiungendo una classe di .checkedai tuoi messaggi di errore dopo la convalida riuscita. Puoi farlo come segue, come dimostrato qui .

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

Come nella demo collegata sopra, uso i CSS per dare a ciascuno span.errorun'immagine X come sfondo, a meno che non abbia la classe .checked, nel qual caso ottiene un'immagine del segno di spunta.

Ecco il mio codice finora:

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."));

Evviva!

Gridare

Ora per quel grido - in origine, il mio codice nascondeva ciecamente i messaggi di errore sugli altri campi corrispondenti invece di convalidarli nuovamente, il che significava che se c'era un altro problema (come "sono consentiti solo i numeri e hai inserito lettere") , è stato nascosto fino a quando l'utente non ha provato a inviare. Questo perché non sapevo come evitare il ciclo di feedback menzionato nei commenti sopra. Sapevo che doveva esserci un modo, quindi ho posto una domanda e Nick Craver mi ha illuminato. Grazie Nick!

Domanda risolta

Questa era originariamente una domanda tipo "fammi condividere questo e vedi se qualcuno può suggerire miglioramenti". Anche se accetterei comunque il feedback, penso che sia abbastanza completo a questo punto. (Potrebbe essere più breve, ma voglio che sia facile da leggere e non necessariamente conciso.) Quindi divertiti!

Aggiornamento: ora fa parte di jQuery Validation

Questo è stato ufficialmente aggiunto a jQuery Validation il 4/3/2012.


Inoltre, consulta la regola strettamente correlata: "O salta questi campi o compila almeno X di essi" - stackoverflow.com/questions/1888976/…
Nathan Long,

Perché un input arbitrario dovrebbe essere responsabile del controllo se altri input sono riempiti? Questo non ha senso. Forse potresti includere un po 'di markup con gli elementi coinvolti?
montrealist

@dalbaeb - Ho chiarito un po 'l'esempio. Non è che un input arbitrario sia responsabile del controllo degli altri; è che ogni input in un gruppo è responsabile del controllo di tutti gli altri.
Nathan Long,

Questo è quello che ho pensato, grazie mille!
montrealist

3
Grazie, per me funziona, ma gli altri campi obbligatori nel modulo ora non rispondono più a meno che non guadagnino e perdano la concentrazione dopo il controllo. (Qualcuno ha aggiunto questo come risposta all'altra tua domanda, ma è stato necessario contrassegnarlo perché non è una risposta).
mydoghasworms

Risposte:


21

Questa è un'ottima soluzione Nathan. Molte grazie.

Ecco un modo per far funzionare il codice sopra, nel caso in cui qualcuno abbia problemi a integrarlo, come ho fatto io:

Codice all'interno del file 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"]
});

Codice all'interno del file 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" />

Non dimenticare di includere il file additional-methods.js!


Sono contento che ti sia utile e grazie per aver fornito informazioni. Tuttavia, invece di utilizzare il metodo addClassRules, preferisco utilizzare una serie di regole su ogni singolo modulo. Se vai su questa pagina ( jquery.bassistance.de/validate/demo/milk ) e fai clic su "mostra script utilizzato in questa pagina" vedrai un esempio. Faccio un ulteriore passo avanti: dichiaro un array chiamato "rules", quindi separatamente, li uso con var validator = $ ('# formtovalidate'). Validate (rules);
Nathan Long,

Un altro pensiero: la classe "fillone" che mostri qui potrebbe essere problematica. Cosa succede se, sullo stesso modulo, è necessario richiedere almeno un numero di parte E almeno un nome di contatto? La tua regola consentirà 0 nomi di contatti purché sia ​​presente almeno un numero di parte. Penso che sia meglio impostare regole come require_from_group: [1,".partnumber"]e ...[1,".contactname"]assicurarti di convalidare le cose giuste.
Nathan Long,

6

Bella soluzione. Tuttavia, ho avuto il problema di altre regole richieste che non funzionavano. L'esecuzione di .valid () sul modulo ha risolto questo problema per me.

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

1
Grazie Sean, anch'io avevo questo problema. C'è un problema con questa soluzione, tuttavia, quando l'utente accede al modulo per la prima volta: non appena compila il primo campo di richiesta dal gruppo, tutti gli altri campi del modulo verranno convalidati e quindi contrassegnati come difettosi. Il modo in cui ho risolto questo problema è stato aggiungere un gestore form.submit () prima di creare un'istanza del validatore, in cui ho impostato un flag validator.formSubmit = true. Nel metodo require-from-group controllo quindi quel flag; se è lì lo faccio $(element.form).valid();, altrimenti lo faccio fields.valid();.
Christof

Qualcuno può spiegare cosa sta realmente accadendo qui? Ho una regola abbastanza simile, che ha funzionato ma in cui non avevamo affrontato il problema della riconvalida (altri campi nel gruppo ancora contrassegnati come non validi). Ma ora sto riscontrando che il modulo viene inviato anche se non valido. Se i campi raggruppati sono validi, non entra nel submithandler e se non è valido entra in invalidHandler ma lo invia comunque! Direi che questo è un bug abbastanza serio nel plug-in di convalida? Il fatto che una regola ritorni valida si applica solo a quella regola (nemmeno all'intero campo), quindi perché viene inviato un modulo non valido?
Adam

Ho studiato ulteriormente e sono i campi prima del gruppo che non convalidano in modo appropriato. Ho chiesto questo come una domanda separata (con una soluzione parziale che ho discoivered): stackoverflow.com/questions/12690898/...
Adam

4

Grazie Sean. Ciò ha risolto il problema che avevo con il codice che ignorava altre regole.

Ho anche apportato alcune modifiche in modo che il messaggio "Compila almeno 1 campo .." venga visualizzato in un div separato invece che dopo ogni campo.

mettere in forma lo script di convalida

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

aggiungilo da qualche parte nella pagina

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

aggiungere alla funzione addMethod del metodo require_from_group

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

4

Ho inviato una patch che non soffre dei problemi della versione corrente (per cui l'opzione "required" smette di funzionare correttamente su altri campi, una discussione dei problemi con la versione corrente è su github .

Esempio su 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."));

3

L'inizio di un nome di variabile con $ è richiesto in PHP, ma piuttosto strano (IMHO) in Javascript. Inoltre, credo che ti riferisci ad esso come "$ modulo" due volte e "modulo" una volta, giusto? Sembra che questo codice non dovrebbe funzionare.

Inoltre, non sono sicuro che sia la normale sintassi del plug-in jQuery, ma potrei aggiungere commenti sopra la chiamata addMethod, spiegando ciò che si ottiene. Anche con la descrizione del testo sopra, è difficile seguire il codice, perché non ho familiarità con quale fieldset,: riempito, valore, elemento o selettore si riferiscono. Forse la maggior parte di questo è ovvio per qualcuno che abbia familiarità con il plug-in Validate, quindi usa il giudizio su quale sia la giusta quantità di spiegazioni.

Forse potresti creare alcune variabili per auto-documentare il codice; piace,

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

(supponendo di aver capito il significato di queste parti del tuo codice! :))

Non sono esattamente sicuro di cosa significhi "modulo", in realtà - c'è un nome più specifico che potresti dare a questa variabile?

Bel codice, nel complesso!


Grazie per i suggerimenti: ho chiarito i nomi delle variabili e suddiviso il codice per renderlo un po 'più leggibile.
Nathan Long,

2

Poiché il modulo su cui sto lavorando ha diverse regioni clonate con input raggruppati come questi, ho passato un argomento aggiuntivo al costruttore require_from_group, cambiando esattamente una riga della tua funzione addMethod:

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

e in questo modo è possibile passare una volta un selettore, un ID o un nome di elemento:

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

e il validatore limiterà la validazione agli elementi con quella classe solo all'interno di ogni fieldset, piuttosto che cercare di contare tutti gli elementi classificati .reqgrp nel form.


2

Ecco la mia idea alla risposta di Rocket Hazmat, cercando di risolvere il problema di altri campi definiti che necessitano di essere convalidati, ma contrassegnando tutti i campi come validi se ne viene riempito uno con successo.

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);
    }
    if (validOrNot) {
    $(selector).each(function() {
            $(this).removeClass('error');
            $('label.error[for='+$(this).attr('id')+']').remove();
        });
    }
    return validOrNot;
}, jQuery.format("Please fill out at least {0} of these fields."));

L'unico problema rimanente con questo ora è il caso limite in cui il campo è vuoto, quindi riempito, quindi nuovamente vuoto ... nel qual caso l'errore verrà applicato al singolo campo e non al gruppo. Ma sembra così improbabile che accada con qualsiasi frequenza e tecnicamente funziona ancora in quel caso.


Non ha senso questa risposta poiché questo metodo / regola è stato integrato nel plug-in nell'aprile 2012.
Sparky

Ho lo stesso problema che ha Rocket Hazmat con il metodo che ora viene fornito con il validatore. Convalida quel gruppo di campi, ma non vengono convalidati altri campi che utilizzano altri metodi. Questa risposta è un tentativo di risolvere questo problema. Se hai una soluzione migliore, fammelo sapere.
squarecandy

Fino a quando lo sviluppatore non risolverà definitivamente il problema, piuttosto che aggiungere confusione, consiglio semplicemente qualsiasi soluzione temporanea approvata qui: github.com/jzaefferer/jquery-validation/issues/412
Sparky

1

Avevo problemi con altre regole che non venivano controllate insieme a questo, quindi ho cambiato:

fields.valid();

A questa:

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

Ho anche apportato alcuni miglioramenti (personali) e questa è la versione che sto 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."));

Funziona facendo convalidare nuovamente gli altri campi, ma ora, quando tutti i campi di un gruppo sono contrassegnati come non validi e ne compili uno, solo quello viene convalidato. Almeno per me?
Christof

@ Chris - guarda la mia nuova risposta che si basa su questa e affronta questo.
squarecandy

0

Grazie Nathan. Mi hai risparmiato un sacco di tempo.

Tuttavia, devo notare che questa regola non è pronta per jQuery.noConflict (). Quindi, è necessario sostituire tutto $ con jQuery per lavorare, ad esempio,var $j = jQuery.noConflict()

E ho una domanda: come farei a comportarsi come una regola incorporata? Ad esempio, se inserisco un messaggio di posta elettronica, il messaggio "Per favore inserisci un indirizzo email valido" scompare automaticamente ma se compilo uno dei campi del gruppo il messaggio di errore rimane.


Hai ragione - non ho considerato la situazione non conflittuale. Potrei aggiornarlo in futuro, ma puoi facilmente eseguire una ricerca e sostituzione, se lo desideri. Per la tua seconda domanda, non vedo lo stesso problema. Con un rapido test, se è richiesto un campo di un gruppo, non appena digito qualcosa, l'intero gruppo passa quella regola. Se è richiesto più di uno, non appena l'ultimo richiesto viene riempito e perde il focus, l'intero gruppo passa. È quello che vedi?
Nathan Long,

hmm per qualche motivo il markup è sbagliato e non ho avuto successo nel risolverlo
Rinat

Rinat: puoi semplificare e restringere il problema? Prova a utilizzare il mio codice su un modulo più semplice che non necessita di modifiche senza conflitti. Fai la forma più semplice su cui puoi testarlo e fallo funzionare prima.
Nathan Long
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.