web-dev-qa-db-fra.com

Utiliser JQuery Validate Plugin pour valider plusieurs champs de formulaire avec des noms identiques

J'ai un formulaire généré dynamiquement avec des champs de saisie du même nom (par exemple: "carte"). Je n'ai pas la possibilité de modifier les noms de champ ou de générer des noms de champ uniques car le code du gestionnaire de formulaire (Perl/CGI) est conçu pour gérer un tableau de valeurs d'entrée (dans ce cas @map).

Comment puis-je utiliser JQuery Validate Plugin pour valider un formulaire dans une telle situation? Plus précisément, je souhaiterais qu'un seul élément du tableau soumis ait une certaine valeur fixe. J'utilise actuellement un gestionnaire d'événements personnalisé qui crée un objet JSON avec serializeArray(), puis le traverse pour s'assurer que la condition est remplie. Mais puisque j'ai utilisé le plugin de validation dans le reste de l'application, je me demandais si un tel cas pouvait être géré en utilisant le même plugin ici aussi.

Merci de votre attention.

47
Ya. Perelman

J'ai passé un peu de temps à chercher et à essayer différentes choses quand j'ai finalement essayé la façon la plus triviale de faire la validation sur plusieurs champs. Chaque champ et ses clones partagent une classe unique à chaque ensemble. Je viens de parcourir les entrées avec cette classe et j'ai ajouté mes règles de validation comme d'habitude. J'espère que cela pourrait aider quelqu'un d'autre.

    $("#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





});
39
jason

Comme je ne peux pas commenter la réponse de @scampbell, je ne sais pas si c'est à propos des points de réputation ou parce que le fil vient de se fermer, j'ai une contribution à sa réponse,

Au lieu de modifier le fichier source jquery.validation, vous pouvez simplement remplacer la fonction que vous devez modifier uniquement dans les pages qui le nécessitent.

un exemple serait:

$.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();
};

ce n'est peut-être pas la meilleure solution, mais au moins cela évite de modifier les fichiers source qui pourraient être remplacés plus tard lors de la sortie d'une nouvelle version. où votre fonction remplacée pourrait ou non se casser

59
Ahmed Galal

Ancien thread que je connais mais je suis tombé dessus à la recherche de la solution au même problème.

Une solution plus élégante a été publiée ici: http://web-funda.blogspot.com/2009/05/jquery-validation-for-array-of-input.html

Vous modifiez simplement jquery.validate.js et changez checkForm en

    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();
}
55
scampbell

Je viens d'apprendre d'un courrier de l'auteur des plugins, Jörn Zaefferer, que la validation nécessite que les noms de champs soient uniques, à l'exception des boutons radio et des cases à cocher.

11
Ya. Perelman

La réponse de Jason fera l'affaire, mais je ne voulais pas ajouter d'événements de clic supplémentaires sur chaque formulaire sur lequel je l'ai fait.

Dans mon cas, le plugin de validation considère les noms se terminant par "[]" différents même s'ils peuvent avoir des noms de champs identiques. Pour ce faire, j'ai remplacé ces deux méthodes internes après le chargement 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);
};
4
Steve Farthing

Voici comment je l'ai fait. Un peu plus facile que les méthodes précédemment proposées:

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;
}

Dans mon cas, j'ai un assistant (de 3 étapes) qui s'est avéré encore plus complexe car je ne veux pas valider tous les champs à la fois. Je place essentiellement les composants dans des onglets et si le premier onglet est valide, je passe au suivant, jusqu'à ce que j'arrive au dernier, après quoi je soumets toutes les données. Ainsi, le paramètre tab contient l'élément tab (qui est un div). Je passe ensuite en revue tous les éléments d'entrée enfants dans mon onglet et je vérifie leur validité.

Tout le reste est standard.


Juste pour être complet, voici le reste du code: comment le formulaire est soumis et à quoi ressemble mon validateur:

<a href="javascript:moveToNextTab(1)" class="button next">Submit</a>

Et voici la fonction js appelée:

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.
}

J'utilise ces règles de validation (que je crée sur 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
        }
    }
});
3
Stef

Utilisez simplement un attribut inutilisé de l'entrée pour stocker le nom d'origine, puis renommez simplement avec son index attaché:

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');
});

}

Ensuite, après avoir défini votre validateur:

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

et lorsque vous avez terminé la validation, revenez en arrière comme ceci:

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

-- J'espère que cela t'aides!

3
Matt Kircher

J'utilise le "plug-in de validation jQuery 1.7".

Le problème pourquoi plusieurs éléments "$ (: input)" partageant le même nom ne sont pas validés

est la méthode $ .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 condition

if (this.name dans rulesCache || .....

évalue pour le deuxième et les éléments suivants partageant le même nom true ....

La solution serait d'avoir la condition:

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

Excusez-moi, JS puritans, que (this.id || this.name) n'est pas à 100% ...

Bien sûr, le

rulesCache [this.name] = true;

la ligne doit également être modifiée de manière appropriée.

La méthode $ .validator.prototype.elements serait donc:

$(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;
        });
    };
}

});

2
Motlicek Petr

Peut-être que je manque le point, mais puisque le validateur ne fonctionne pas avec plusieurs noms (essayé ... échoué!) J'ai changé mon formulaire pour changer dynamiquement les noms, définir les règles, puis annuler les noms lors de la soumission.

Deux méthodes (ignorer les trucs wlog, ça sort juste sur la console):

// 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-<name>-<id>", e.g. 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);
            }
        });
    }
}

Lors du chargement du formulaire, et chaque fois que j'ajoute dynamiquement une autre ligne, j'appelle la méthode set, par ex.

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

Cela change les noms pour permettre une validation individuelle.

Dans le submitHandler: thingumy après validation, j'appelle le retour, c'est-à-dire.

revertGeneratedNames(form, ['amounts']);

Ce qui ramène les noms aux originaux avant de publier les données.

1
Alfonzo

Pour moi, cela a été résolu très facilement en désactivant le débogage

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

Je pense que vous avez mal compris le fonctionnement des formulaires HTML. Chaque élément de formulaire doit avoir un nom unique, à l'exception de plusieurs cases à cocher et boutons qui vous permettent de choisir une/plusieurs options pour un champ de données.

Dans votre cas, non seulement la validation JQuery, mais également un validateur de formulaire côté serveur échoueraient, car il ne peut pas affecter les entrées aux champs de données. Supposons que vous souhaitiez que l'utilisateur entre le nom, le prénom, l'adresse e-mail, le fax (facultatif) et que tous vos champs de saisie aient name="map"

Ensuite, vous recevrez ces listes sur soumettre:

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

Vous voyez qu'il est impossible de valider ce formulaire de manière fiable.

Je recommande de revoir la documentation de votre gestionnaire de formulaire Perl ou de l'adapter si vous l'avez écrit vous-même.

0
Franz