jQuery Validate - erfordert, dass mindestens ein Feld in einer Gruppe ausgefüllt wird

98

Ich verwende das ausgezeichnete jQuery Validate Plugin , um einige Formulare zu validieren. In einem Formular muss ich sicherstellen, dass der Benutzer mindestens eines aus einer Gruppe von Feldern ausfüllt. Ich denke, ich habe eine ziemlich gute Lösung und wollte sie teilen. Bitte schlagen Sie Verbesserungen vor, die Sie sich vorstellen können.

Als ich keine eingebaute Möglichkeit fand, dies zu tun, suchte und fand ich Rebecca Murpheys benutzerdefinierte Validierungsmethode , die sehr hilfreich war.

Ich habe dies auf drei Arten verbessert:

  1. Damit Sie einen Selektor für die Feldgruppe übergeben können
  2. Damit können Sie angeben, wie viele dieser Gruppen gefüllt werden müssen, damit die Validierung erfolgreich ist
  3. Anzeigen aller Eingaben in der Gruppe als bestandene Validierung, sobald eine von ihnen die Validierung besteht. (Siehe Gruß an Nick Craver am Ende.)

Sie können also sagen, dass mindestens X Eingänge, die mit dem Selektor Y übereinstimmen, gefüllt sein müssen.

Das Endergebnis mit einem solchen Markup:

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

... ist eine Gruppe von Regeln wie diese:

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

Bei Punkt 3 wird davon ausgegangen, dass Sie .checkedIhren Fehlermeldungen nach erfolgreicher Validierung eine Klasse von hinzufügen . Sie können dies wie folgt tun, wie hier gezeigt .

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

Wie in der oben verlinkten Demo verwende ich CSS, um jedem span.errorein X-Bild als Hintergrund zu geben, es sei denn, es hat die Klasse .checked. In diesem Fall erhält es ein Häkchenbild.

Hier ist mein Code bisher:

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!

Ruf raus

Nun zu diesem Shout-Out - ursprünglich hat mein Code die Fehlermeldungen in den anderen übereinstimmenden Feldern nur blind ausgeblendet, anstatt sie erneut zu validieren. Dies bedeutete, dass bei einem anderen Problem (wie "Nur Zahlen sind zulässig und Sie haben Buchstaben eingegeben") wurde es ausgeblendet, bis der Benutzer versuchte, es einzureichen. Dies lag daran, dass ich nicht wusste, wie ich die in den obigen Kommentaren erwähnte Rückkopplungsschleife vermeiden sollte. Ich wusste, dass es einen Weg geben musste, also stellte ich eine Frage und Nick Craver erleuchtete mich. Danke, Nick!

Frage gelöst

Dies war ursprünglich eine Art Frage "Lassen Sie mich dies teilen und sehen, ob jemand Verbesserungen vorschlagen kann". Obwohl ich Feedback immer noch begrüßen würde, denke ich, dass es zu diesem Zeitpunkt ziemlich vollständig ist. (Es könnte kürzer sein, aber ich möchte, dass es leicht zu lesen und nicht unbedingt prägnant ist.) Also einfach genießen!

Update - jetzt Teil der jQuery-Validierung

Dies wurde am 03.04.2012 offiziell zur jQuery-Validierung hinzugefügt.

Nathan Long
quelle
Siehe auch eng verwandte Regel - "Überspringen Sie entweder diese Felder oder füllen Sie mindestens X davon" - stackoverflow.com/questions/1888976/…
Nathan Long
Warum sollte eine beliebige Eingabe dafür verantwortlich sein, zu prüfen, ob andere Eingaben gefüllt sind? Das macht keinen Sinn. Vielleicht könnten Sie den beteiligten Elementen ein bisschen Markup hinzufügen?
Montrealist
@ Dalbaeb - Ich habe das Beispiel ein wenig geklärt. Es ist nicht so, dass eine beliebige Eingabe für die Überprüfung anderer verantwortlich ist. Es ist so, dass jede Eingabe in einer Gruppe dafür verantwortlich ist, alle anderen zu überprüfen.
Nathan Long
Das habe ich mir gedacht, vielen Dank!
Montrealist
3
Danke, das funktioniert bei mir, aber die anderen erforderlichen Felder im Formular reagieren jetzt nicht mehr, es sei denn, sie gewinnen und verlieren nach der Prüfung den Fokus. (Jemand hat dies als Antwort auf Ihre andere Frage hinzugefügt, aber es musste markiert werden, da es keine Antwort ist.)
Mydoghaswürmer

Antworten:

21

Das ist eine hervorragende Lösung, Nathan. Vielen Dank.

Hier ist eine Möglichkeit, wie der obige Code funktioniert, falls jemand Probleme bei der Integration hat, wie ich es getan habe:

Code in der Datei 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"]
});

Code in der HTML- Datei:

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

Vergessen Sie nicht, die Datei Additional-Methods.js einzuschließen!


quelle
Ich bin froh, dass es für Sie hilfreich ist, und danke, dass Sie Informationen eingegeben haben. Anstatt die addClassRules-Methode auszuführen, bevorzuge ich jedoch die Verwendung eines Array von Regeln für jedes einzelne Formular. Wenn Sie zu dieser Seite ( jquery.bassistance.de/validate/demo/milk ) gehen und auf "Auf dieser Seite verwendetes Skript anzeigen " klicken, sehen Sie ein Beispiel. Ich gehe noch einen Schritt weiter: Ich deklariere ein Array namens "rules" und verwende es dann separat mit var validator = $ ('# formtovalidate'). Validate (rules);
Nathan Long
Ein anderer Gedanke: Die hier gezeigte 'Fillone'-Klasse könnte problematisch sein. Was ist, wenn Sie auf demselben Formular mindestens eine Teilenummer UND mindestens einen Kontaktnamen benötigen? Ihre Regel erlaubt 0 Kontaktnamen, solange mindestens eine Teilenummer vorhanden ist. Ich denke, es ist besser, Regeln festzulegen require_from_group: [1,".partnumber"]und ...[1,".contactname"]sicherzustellen, dass Sie die richtigen Dinge validieren.
Nathan Long
6

Schöne Lösung. Ich hatte jedoch das Problem, dass andere erforderliche Regeln nicht funktionierten. Das Ausführen von .valid () für das Formular hat dieses Problem für mich behoben.

if(!$(element).data('being_validated')) {
  var fields = $(selector, element.form);
  fields.data('being_validated', true); 
  $(element.form).valid();
  fields.data('being_validated', false);
}
Sean
quelle
1
Danke Sean, ich hatte auch dieses Problem. Es gibt jedoch ein Problem mit dieser Lösung, wenn der Benutzer zum ersten Mal zum Formular gelangt. Sobald er das erste Feld für die Anforderung aus der Gruppe ausfüllt, werden alle anderen Formularfelder validiert und somit als fehlerhaft markiert. Ich habe dies gelöst, indem ich einen form.submit () - Handler hinzugefügt habe, bevor ich eine Instanz des Validators erstellt habe, in der ich ein Flag gesetzt habe validator.formSubmit = true. In der Require-from-Group-Methode überprüfe ich dann, ob dieses Flag vorhanden ist. Wenn es da ist, mache ich es $(element.form).valid();, sonst mache ich es fields.valid();.
Christof
Kann jemand erklären, was hier eigentlich passiert? Ich habe eine ziemlich ähnliche Regel, die funktioniert hat, in der wir jedoch das Revalidierungsproblem nicht angesprochen haben (andere Felder in der Gruppe sind immer noch als ungültig markiert). Aber jetzt habe ich die Erfahrung gemacht, dass das Formular auch dann gesendet wird, wenn es ungültig ist. Wenn die gruppierten Felder gültig sind, wird der Submithandler nicht eingegeben, und wenn er ungültig ist, wird der ungültige Handler eingegeben, aber er wird trotzdem gesendet! Ich würde sagen, dies ist ein ziemlich schwerwiegender Fehler im Validierungs-Plugin. Dass eine Regel gültig zurückgibt, gilt nur für diese Regel (nicht einmal für das gesamte Feld). Warum wird ein ungültiges Formular gesendet?
Adam
Ich habe weiter nachgeforscht und es sind Felder vor der Gruppe, die nicht ordnungsgemäß validieren. Ich habe dies als separate Frage gestellt (mit einer teilweisen Problemumgehung, die ich nicht erkannt habe): stackoverflow.com/questions/12690898/…
Adam
4

Danke Sean. Dadurch wurde das Problem behoben, bei dem der Code andere Regeln ignorierte.

Ich habe auch einige Änderungen vorgenommen, damit die Meldung "Bitte mindestens 1 Feld ausfüllen .." in einem separaten Div angezeigt wird, anstatt in jedem Feld.

in Form validieren Skript setzen

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

füge dies irgendwo auf der Seite hinzu

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

Fügen Sie der Methode require_from_group die Funktion addMethod hinzu

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

Ich habe einen Patch eingereicht , der nicht unter den Problemen der aktuellen Version leidet (wobei die Option "Erforderlich" in anderen Bereichen nicht mehr ordnungsgemäß funktioniert). Eine Diskussion der Probleme mit der aktuellen Version findet auf Github statt .

Beispiel unter 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."));
docflabby
quelle
3

Das Starten eines Variablennamens mit $ ist in PHP erforderlich, in Javascript jedoch ziemlich seltsam (IMHO). Ich glaube auch, dass Sie es zweimal als "$ module" und einmal als "module" bezeichnen, oder? Es scheint, dass dieser Code nicht funktionieren sollte.

Ich bin mir auch nicht sicher, ob es sich um die normale Syntax des jQuery-Plugins handelt, aber ich kann über Ihrem addMethod-Aufruf Kommentare hinzufügen, um zu erklären, was Sie erreichen. Selbst mit Ihrer obigen Textbeschreibung ist es schwierig, dem Code zu folgen, da ich nicht weiß, auf welche Feldmenge: gefüllt, Wert, Element oder Selektor sich beziehen. Vielleicht ist das meiste davon für jemanden offensichtlich, der mit dem Validate-Plugin vertraut ist. Beurteilen Sie also, was die richtige Menge an Erklärungen ist.

Vielleicht könnten Sie ein paar Variablen ausbrechen, um den Code selbst zu dokumentieren. mögen,

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

(Vorausgesetzt, ich habe die Bedeutung dieser Teile Ihres Codes verstanden! :))

Ich bin mir nicht ganz sicher, was "Modul" eigentlich bedeutet - gibt es einen genaueren Namen, den Sie dieser Variablen geben könnten?

Insgesamt ein schöner Code!

Michael Gundlach
quelle
Vielen Dank für die Vorschläge - ich habe die Variablennamen geklärt und den Code aufgeschlüsselt, um ihn besser lesen zu können.
Nathan Long
2

Da das Formular, an dem ich arbeite, mehrere geklonte Regionen mit gruppierten Eingaben wie diesen enthält, habe ich ein zusätzliches Argument an den Konstruktor require_from_group übergeben und genau eine Zeile Ihrer Funktion addMethod geändert:

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

Auf diese Weise kann ein Selektor, eine ID oder ein Elementname einmal übergeben werden:

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

und der Validator beschränkt die Validierung auf Elemente mit dieser Klasse nur innerhalb jeder Feldmenge, anstatt zu versuchen, alle klassifizierten .reqgrp-Elemente im Formular zu zählen.

Andrew Roazen
quelle
2

Hier ist mein Riss bei der Antwort von Rocket Hazmat: Ich versuche, das Problem anderer definierter Felder zu lösen, die ebenfalls validiert werden müssen, markiere aber alle Felder als gültig, wenn eines erfolgreich ausgefüllt wurde.

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

Das einzige verbleibende Problem dabei ist jetzt der Randfall, in dem das Feld leer, dann gefüllt und dann wieder leer ist. In diesem Fall wird der Fehler auf das einzelne Feld und nicht auf die Gruppe angewendet. Aber das scheint so unwahrscheinlich, dass es mit irgendeiner Frequenz passiert, und es funktioniert in diesem Fall technisch immer noch.

squarecandy
quelle
Diese Antwort hat keinen Sinn, da diese Methode / Regel bereits im April 2012 in das Plugin integriert wurde.
Sparky
Ich habe das gleiche Problem wie Rocket Hazmat mit der Methode, die jetzt mit Validator geliefert wird. Es wird überprüft, ob eine Gruppe von Feldern, aber keine anderen Felder, die andere Methoden verwenden, überprüft werden. Diese Antwort ist ein Versuch, dieses Problem zu lösen. Wenn Sie eine bessere Lösung haben, lassen Sie es mich bitte wissen.
Squarecandy
Bis der Entwickler das Problem dauerhaft behebt, anstatt Verwirrung zu stiften, empfehle ich einfach, welche temporäre Lösung hier gebilligt wird: github.com/jzaefferer/jquery-validation/issues/412
Sparky
1

Ich hatte Probleme mit anderen Regeln, die im Zusammenhang damit nicht überprüft wurden. Deshalb habe ich Folgendes geändert:

fields.valid();

Dazu:

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

Ich habe auch einige (persönliche) Verbesserungen vorgenommen, und dies ist die Version, die ich verwende:

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."));
Rakete Hazmat
quelle
Funktioniert, um die anderen Felder erneut zu validieren. Wenn jedoch alle Felder einer Gruppe als ungültig markiert sind und Sie eines ausfüllen, wird nur dieses validiert. Zumindest für mich?
Christof
@ Chris - siehe meine neue Antwort, die auf dieser aufbaut und diese anspricht.
Squarecandy
0

Danke, Nathan. Du hast mir eine Menge Zeit gespart.

Ich muss jedoch beachten, dass diese Regel nicht bereit für jQuery.noConflict () ist. Man muss also alle $ durch jQuery ersetzen, um beispielsweise damit arbeiten zu können.var $j = jQuery.noConflict()

Und ich habe die Frage: Wie würde ich dafür sorgen, dass es sich wie eine eingebaute Regel verhält? Wenn ich beispielsweise eine E-Mail eingebe, verschwindet die Meldung "Bitte geben Sie eine gültige E-Mail-Adresse ein" automatisch, aber wenn ich eines der Gruppenfelder ausfülle, bleibt die Fehlermeldung bestehen.

Rinat
quelle
Sie haben Recht - ich habe die Konfliktsituation nicht berücksichtigt. Ich kann das in Zukunft aktualisieren, aber Sie können leicht ein Suchen und Ersetzen durchführen, wenn Sie möchten. Bei Ihrer zweiten Frage sehe ich nicht das gleiche Problem. Wenn bei einem Schnelltest ein Feld aus einer Gruppe erforderlich ist, besteht die gesamte Gruppe diese Regel, sobald ich etwas eingebe. Wenn mehr als eine benötigt wird, sobald die letzte erforderliche gefüllt ist und den Fokus verliert, besteht die gesamte Gruppe. Sehen Sie das?
Nathan Long
hmm aus irgendeinem Grund ist Markup vermasselt und ich hatte keinen Erfolg bei der Reparatur
Rinat
Rinat - können Sie das Problem vereinfachen und eingrenzen? Versuchen Sie, meinen Code in einem einfacheren Formular zu verwenden, für das keine Änderungen erforderlich sind. Machen Sie das einfachste Formular, auf dem Sie es möglicherweise testen können, und bringen Sie es zuerst zum Laufen.
Nathan Long