web-dev-qa-db-fra.com

Conflit entre jQuery Validate et Masked Input

J'ai un formulaire qui utilise les champs jQuery Validate et Masked Input pour les champs de numéro de téléphone et de code postal américain.

Par exemple, pour un code postal américain, l'option Entrée masquée autorise uniquement la saisie de nombres et impose le format "99999" ou "99999-9999" (où 9 peut être n'importe quel nombre). La règle Valider requiert la même chose. Mais dans certains cas, Valider marque un champ comme non valide alors qu'il devrait être valide.

Spécificités du code

La regex que jQuery Validate utilise dans le champ Code postal est ^\d{5}$|^\d{5}\-\d{4}$.

Le masque que j'applique avec l'entrée masquée est .mask('99999?-9999')

Étapes pour reproduire

Un conflit entre eux se produit lorsque je fais ce qui suit:

  • Remplissez un code postal invalide (par exemple, trois chiffres)
  • Tab loin du champ. L'entrée masquée efface l'entrée (comportement attendu) et jQuery Validate la marque comme non valide car elle est vide (également attendue).
  • Retournez au champ Zip et renseignez un 5 chiffres valide Zip.
  • Tab loin du champ. Il est toujours marqué comme invalide. C'est inattendu .

Ce problème se produit not si je remplis un fichier Zip à 9 chiffres. 

Hypothèse

Je pense que cette erreur est due au fait que, dans le cas du code Zip à 5 chiffres, l’entrée masquée a temporairement inséré "-____" pour indiquer à l’utilisateur qu’il peut éventuellement entrer un tiret et quatre autres chiffres. Ceci est supprimé en cas de flou, mais avant qu'il ne soit supprimé, le champ est validé et échoue, car les traits de soulignement ne sont pas autorisés.

Cette hypothèse est étayée par le fait que, si le formulaire est ensuite validé à nouveau, le champ Zip passe. J'ai fait cela de deux manières:

  • En soumettant le formulaire; tous les champs sont re-validés lorsque l'utilisateur clique sur le bouton d'envoi, que le champ Zip est transmis et que le formulaire est soumis.
  • En configurant un événement blur qui valide à nouveau ce champ spécifique. Par exemple:

    $ ("# code postal"). blur (function () {$ (this) .closest ('form'). validate (). element ($ (this));}); 

Cela peut servir de solution de hacky , mais n'est pas très satisfaisant, car 1) les paramètres par défaut sont déjà validés à nouveau sur le flou, ce qui est répétitif, et 2) il nécessite du code supplémentaire en plus de la validation normale règles.

Quelqu'un d'autre a-t-il rencontré ce problème? Avez-vous une solution plus élégante que la configuration d’un écouteur d’événement de flou supplémentaire?

Mise à jour: application de la solution hacky

Même appliquer la solution ci-dessus ne fonctionne pas aussi bien que je le souhaiterais. Par exemple, cela ne fonctionne pas:

$appDiv.delegate('input,select,textarea','blur',function(){
  $(this).closest('form').validate().element($(this));
});

... ni cela:

$('input,select,textarea').live('blur',function(){
  $(this).closest('form').validate().element($(this));
});

... mais cela fait:

$('input,select,textarea').each(function(){
  $(this).blur(function(){
    $(this).closest('form').validate().element($(this));
  });
});

Étant donné que ces éléments sont chargés par AJAX, la version .each doit être exécutée chaque fois qu'une section de formulaire est chargée.

25
Nathan Long

Je cherchais en fait une réponse à cette question précise. Nous avons fini par trouver une solution plus fiable. 

Étant donné que je définis ma propre méthode de validation pour le code postal, je l'ai modifiée de manière à supprimer le trait d'union si la longueur du code postal était de 6 après avoir supprimé le paramètre fictif de la valeur.

Cela ressemblait à ceci:

$.validator.addMethod("zipcode", function(postalcode, element) {
            //removes placeholder from string
            postalcode = postalcode.split("_").join("");

            //Checks the length of the zipcode now that placeholder characters are removed.
            if (postalcode.length === 6) {
                //Removes hyphen
                postalcode = postalcode.replace("-", "");
            }
            //validates postalcode.
            return this.optional(element) || postalcode.match(/^\d{5}$|^\d{5}\-\d{4}$/);
        }, "Please specify a valid Zip code");

Ainsi, le code postal que je valide aura les espaces réservés ajoutés par le plug-in d'entrée et le trait d'union supprimé si le code postal saisi ne comporte que 5 chiffres (6, y compris le trait d'union). Donc, il va valider correctement.

9
Adrian

J'ai essayé la solution suivante, et cela fonctionne comme un charme.

$("[data-input-type=phone]", "body")
  .mask("(999) 999 99 99")
  .bind("blur", function () {
    // force revalidate on blur.

    var frm = $(this).parents("form");
    // if form has a validator
    if ($.data( frm[0], 'validator' )) {
      var validator = $(this).parents("form").validate();
      validator.settings.onfocusout.apply(validator, [this]);
    }
  });

Ce qui déclenche le problème est la commande d'événements. Lorsqu'un élément est masqué, il est validé par le plugin maskedinput sur blur, mais le même élément est validé par le plugin validator sur l'événement focusout (qui est un wrapper pour rendre flou les non-ie navigateurs) qui s’appelle avant le flou masqué.

Dans cette situation, l'élément input a la valeur "(___) ___ __ __" lorsque le validateur vérifie la valeur. Lorsque le code atteint l'événement de flou de maskedinput, le plug-in teste et efface la valeur, car il ne s'agit pas d'une entrée valide.

Le résultat de la validation peut être différent pour chaque cas de validation. Par exemple, les règles required passeront avec succès puisque l'élément a une valeur. les champs number non requis échoueront même si nous laissons l'entrée vide, car un masque comme "999" peut être testé en tant que 12_

le code ci-dessus teste si une validation est attachée au formulaire d'entrée masqué et rappelle le gestionnaire d'événements de focusout. Étant donné que notre gestionnaire est attaché au plus tard, nous espérons qu'il sera enfin appelé.

Un avertissement, le code copie simplement le comportement du plugin de validation. Cela fonctionnera probablement pendant une décennie, mais pourrait échouer si le plug-in de validation décide de faire les choses différemment.

cordialement

11
edokan

Connectez-vous simplement à la fonction mask () et ajoutez une logique supplémentaire pour activer votre propre logique de flou après la logique de flou du masque par défaut:

//Store the original mask function
var origMaskFn = $.fn.mask;

$.fn.mask = function (mask, settings) {

    //Call the original function applying the default settings
    origMaskFn.apply(this, [mask, settings]);

    //Manually validate our element on blur to prevent unobtrusive messages
    //from showing before the mask is properly scrubbed
    $(this).bind('blur', function () {
        var $form = $(this).parents('form');
        if (!$form.exists())
            return;

        var validator = $form.validate().element($(this));
    });
}

De plus, vous remarquerez peut-être la fonction 'existe ()'. C’est quelque chose que j’utilise beaucoup avec mes sélecteurs jQuery:

//Function to detect if a jQuery elements exists.  i.e.:
//      var $element = $(selector);
//      if($element.exists()) do something...

$.fn.exists = function () {
    return this.length !== 0;
}
7
Greg H

J'ai eu un problème similaire et j'ai réussi à le résoudre en remplaçant le caractère fictif par défaut par une chaîne vide.

J'utilisais une .mask('9?99'); et je validais avec une simple règle de nombre, et j'avais le même genre de conflit.

J'ai changé la déclaration de masque en .mask('9?99',{placeholder:''}); et ... plus aucun conflit. :)

Il y a un problème possible dans votre cas, à cause du - dans le code postal, qui est toujours inséré dans le formulaire par le plugin mask. Je pense que vous pouvez changer l'expression rationnelle en ^\d{5}\-$|^\d{5}\-\d{4}$ (correspondant au tiret dans les deux cas) et il devrait alors être validé.

Quoi qu'il en soit, vous avez posté il y a quelque temps et vous n'en avez probablement plus besoin, mais cela aidera peut-être quelqu'un d'autre. :)

5
Miguel Pinto

Est-ce que l'ordre que vous faites est important? Par exemple, y a-t-il une différence entre

$(function() {
  var $form = $("#myForm"), $Zip = $("#Zip");
  $Zip.mask("99999?-9999");
  $form.validate();
});

et

$(function() {
  var $form = $("#myForm"), $Zip = $("#Zip");
  $form.validate();
  $Zip.mask("99999?-9999");
});

Je pense que les reliures devraient tirer en ordre.

1
Jarrett Meyer

J'ai essayé certaines des solutions mais aucune n'a fonctionné pour moi. J'utilise "bootstrapValidator" ( http://bootstrapvalidator.com/ ) et "jquery.maskedinput" ( http://digitalbush.com/ projects/masked-input-plugin/ ) donc si quelqu'un a toujours ce problème, ma solution était:

Mon entrée a été marquée comme ceci:

<input type="text" class="form-control" id="telefone" placeholder="(__) ____-_____" name="telefone" required>

Et le script complet comme ceci:

$(document).ready(function() {
   var telefone = $('#telefone'), //THAT'S MY INPUT WITH MASK
       form = telefone.parents("form"), //THAT'S MY FORM WITH THE VALIDATE
       alteredField = false; //THAT'S A FLAG TO SSE IF THE INPUT WAS ALTERED

            // APPLY MY MASK
            telefone.mask('(99) 9999-9999?9');

            // FOCUS ON ANY INPUT
            form.find('input[type=text]').focus(function(){
                // REMOVE THE VALIDATION STATUS
                $(form).data('bootstrapValidator').updateStatus($(this).attr('name'), 'NOT_VALIDATED');
                // ENABLE THE SUBMIT BUTTON
                $(form).data('bootstrapValidator').disableSubmitButtons(false);
            });

            // FOCUS ON THE MASKED INPUT
            telefone.focus(function(){
                // DISABLE THE VALIDATE
                $(form).data('bootstrapValidator').enableFieldValidators('telefone', false);
                // ENABLE THE SUBMIT BUTTON
                $(form).data('bootstrapValidator').disableSubmitButtons(false);
            }).blur(function(){ // BLUR ON THE MASKED INPUT
                // GET THE INPUT VALUE
                var value = telefone.val();
                // ENABLE THE VALIDATE 
                $(form).data('bootstrapValidator').enableFieldValidators('telefone', true);
                // CHECK IF THE VALUE IS EMPTY
                if(value != ""){
                    // CHANGE THE STATUS OF THE INPUT TO VALID 
                    $(form).data('bootstrapValidator').updateStatus('telefone', 'VALID')
                    // ACTIVE THE FLAG
                    alteredField = true;
                }else if (alteredField) { // IF THE INPUT WAS ALTERED BEFORE AND DON'T HAVE VALUE
                    // CHANGE THE STATUS OF THE INPUT TO INVALID
                    $(form).data('bootstrapValidator').updateStatus('telefone', 'INVALID')
                };
            });

        });

Ce code modifie le comportement standard de 'bootstrapValidator', mais était le seul moyen pour résoudre le bogue. 

Je ne connais pas les problèmes de performances, veuillez donc modifier et améliorer le code ^^ J'espère que cela vous aidera ^ _ ^

0