web-dev-qa-db-fra.com

Comment puis-je bloquer jQuery AJAX appel sans asynchrone = faux?

J'ai une page qui AJAX valide un e-mail avant de passer à la page suivante, à l'aide de la méthode HTML5 setCustomValidity () [à l'aide de la bibliothèque webshims pour les navigateurs plus anciens].

Pour que cela fonctionne, j'ai défini l'option async sur false dans l'appel $ .ajax () pour la rendre synchrone, bloquant la page en attente de la réponse AJAX, sinon le formulaire est envoyé avant le retour de l'appel ajax rendant la validation inefficace.

<script>
  function validateEmailRegistered(input) {
    if (input.setCustomValidity === undefined) return;
    var err = 'Email address not found';
    $.ajax({
      url: 'email-is-registered.php', 
      data: { email: input.value }, 
      async: false, 
      success: function(data) {
        var ok = data=='true';
        input.setCustomValidity(ok ? '' : err)
      }
    });
  }
</script>

<form method="get" action="nextpage.php">
  <input name="email" id="email" type="email" required
    onChange="validateEmailRegistered(this)">
  <button type="submit">go</button>
<form>

Je vois que l'option asynchrone doit être obsolète à partir de jQuery 1.8.

Comment puis-je réaliser cette action de blocage sans l'option async?

14
ChrisV

http://bugs.jquery.com/ticket/11013#comment:40

L'utilisation de la fonctionnalité Deferred/Promise dans les requêtes ajax synchrones est déconseillée dans la version 1.8. La méthode $ .ajax avec async: false est prise en charge, mais vous devez utiliser un paramètre de rappel plutôt qu'une méthode Promise telle que .then ou .done.

Ainsi, si vous utilisez les gestionnaires de succès/complet/erreur, vous pouvez toujours utiliser async:false. Plus d'infos sur le billet ci-dessus.

10
Mike

Depuis jQuery 1.8, l'utilisation de async: false avec jqXHR ($ .Deferred) est déconseillée; vous devez utiliser les rappels complets/succès/erreur.

http://api.jquery.com/jQuery.ajax/ (section asynchrone)

J'ai trouvé cela un peu gênant ... les développeurs devraient avoir le choix de faire un appel bloquant avec async: false si c'est ce que la plate-forme permet - pourquoi le restreindre? Je viens de définir un délai d'attente pour minimiser l'impact d'un blocage.

Néanmoins, j'utilise une file d'attente maintenant en 1.8, qui n'est pas bloquante et qui fonctionne plutôt bien. Sébastien Roch a créé un utilitaire facile à utiliser qui vous permet de mettre en file d'attente les fonctions et de les exécuter/les mettre en pause. https://github.com/mjward/Jquery-Async-queue

    queue = new $.AsyncQueue();
    queue.add(function (queue) { ajaxCall1(queue); });
    queue.add(function (queue) { ajaxCall2(queue); });
    queue.add(function() { ajaxCall3() });
    queue.run();

Dans les 2 premières fonctions, je passe l'objet queue dans les appels, voici à quoi ressembleraient les appels:

function ajaxCall1(queue) {
    queue.pause();
    $.ajax({
       // your parameters ....
       complete: function() {
         // your completing steps
         queue.run();
       }
    });
}

// similar for ajaxCall2

Notez que queue.pause(); au début de chaque fonction et queue.run() pour continuer l'exécution de la file d'attente à la fin de votre instruction complete.

2
sonjz

Vous pouvez le faire de manière asynchrone.

Créez une variable globale, que j'appelle ajax_done_and_successful_flag, que vous initialisez à false au début du programme. Vous définissez ce paramètre sur true ou false à divers endroits de vos fonctions Ajax, telles que votre fonction de réussite Ajax ou votre fonction d'erreur Ajax.

Ensuite, vous devez ajouter un gestionnaire d'envoi au bas de votre fonction de validation.

submitHandler: function(form) {
   if (ajax_done_and_successful_flag === true) {
     form.submit()
   }
}  

Le problème est que le code ne s'exécute pas de manière linéaire.
Mettez un tas d'instructions console.log de Firebug dans votre code.
Observez la séquence d'exécution. Vous verrez que votre
La réponse Ajax reviendra en dernier ou à tout moment.
C'est pourquoi vous avez besoin de submitHandler AND du drapeau mondial
pour forcer la fonction de validation à attendre Ajax correct
avant la soumission du formulaire.

Toute sortie à l'écran de la réponse Ajax doit être effectuée dans les fonctions Ajax, telles que la fonction de réussite et la fonction d'erreur.
Vous devez écrire au même emplacement que les fonctions succès/erreur de la fonction de validation.
De cette façon, les messages d'erreur Ajax se fondent dans la fonction d'erreur de la fonction de validation.
Ce concept peut sembler un peu délicat.
L’idée à garder à l’esprit est que les fonctions de succès et d’erreur de la fonction de validation écrivent au même emplacement que les fonctions de succès et d’erreur de l’appel Ajax. .
L'emplacement de mes messages d'erreur est juste à côté de l'endroit où l'utilisateur tape l'entrée. Cela crée une expérience utilisateur agréable que je pense que vous demandez.

Voici mon exemple de code. Je l'ai simplifié.

J'exécute jQuery-1.7.1
et le plug-in de validation jQuery 1.6
J'utilise Firefox 14.0.1 et je l’ai également essayé avec Chrome 21.0 avec succès.

 var ajax_done_and_successful_flag = false;

 // Add methods
  ...

 $.validator.addMethod("USERNAME_NOT_DUPLICATE", function (value, element) {
   return this.optional(element) || validate_username_not_duplicate( );
  },
  "Duplicate user name.");

// validate
$(document).ready(function ( ) {
   $('#register_entry_form form').validate({
      rules: {
         username: {
         required: true,
         minlength: 2,
         maxlength: 20,
         USERNAME_PATTERN: true,
         USERNAME_NOT_DUPLICATE: true
        },    
    ...
    errorPlacement: function (error, element) { 
       element.closest("div").find(".reg_entry_error").html(error);
    },
    success: function(label) {
       label.text('Success!');
    } ,

    submitHandler: function(form) {   
       if (ajax_done_and_successful_flag === true ) {    
          form.submit();
       }
    }
  });
});

/* validation functions*/

function validate_username_not_duplicate() {

   var username_value = $('#username').val(); // whatever is typed in

   $.ajax({
      url: "check_duplicate_username.php",
      type: "POST",
      data: { username: username_value },
      dataType: "text",
      cache: false,
      //async: false,
      timeout: 5000,
      error: function (jqXHR, errorType, errorMessage) {
         ajax_done_and_successful_flag = false;

        if ( errorType === "timeout" ) {
           $('#username').closest("div").find(".reg_entry_error").html("Server timeout, try again later" );
        } else if ...

      },

      success:  function (retString, textStatus,jqXRH) {

        if ( retString === "yes") { // duplicate name 
           // output message next to input field        
           $('#username').closest("div").find(".reg_entry_error").html("Name already taken.");
           ajax_done_and_successful_flag = false;
        } else if ( retString === "no") { // name is okay
           // output message next to input field
           $('#username').closest("div").find(".reg_entry_error").html("success!");
           ajax_done_and_successful_flag = true;

        } else {
           console.log("in validate_username_duplicate func, success function, string returned was not yes or no." );
           $('#username').closest("div").find(".reg_entry_error").html("There are server problems. Try again later.");
           ajax_done_and_successful_flag = false;
       }
     } // end success function

   }); // end ajax call


  return true; // automatically return true, the true/false is handled in
              // the server side program and then the  submit handler

}

Reg_entry_error est l'endroit juste à côté de l'entrée de texte. Voici un exemple de code simplifié du formulaire.

  <label class="reg_entry_label" for="username">Username</label>
  <input class="reg_entry_input" type="text" name="username" id="username" value="" />
  <span class="reg_entry_error" id="usernameError" ></span>  

J'espère que cela répond à votre question.

0
Ken Johnson

Je suppose que vous ferez quand même la validation complète du formulaire lors de la soumission, alors peut-être que la validation du courrier électronique au fur et à mesure est interrompue et que la soumission du formulaire donne la priorité.

Je pense que ce que je ferais serait d'annuler la demande 'validateEmailRegistered' AJAX lorsque l'utilisateur soumettrait le formulaire. Effectuez ensuite la validation complète du formulaire côté serveur, comme d'habitude, y compris la validation du courrier électronique.

D'après ce que je sais de la validation au fur et à mesure, c'est une finesse, et non un substitut à la validation du formulaire lors de sa soumission. Cela n'a donc aucun sens pour moi de bloquer la soumission du formulaire.

0
Jonathan Nicol

Si vous êtes vraiment prêt à ne pas les laisser soumettre le formulaire tant que vous n'avez pas vérifié la validité de l'email, lancez votre bouton d'envoi avec l'attribut disabled, puis réglez le callback $('form button').removeAttr('disabled');

Cela étant dit, je suis avec les autres personnes - laissez-les simplement soumettre le formulaire! Habituellement, les gens font les choses correctement et vous les transmettez sans erreurs ...

0
Ryley

Je pense que vous devez changer un peu comment cela fonctionne. N'essayez pas de bloquer, mais optez pour le non-blocage.

Je voudrais faire comme ceci: - Conserver la validation sur email; rendez-le asynchrone. Lorsqu'il est valide, définissez un indicateur quelque part dans une variable pour savoir qu'il est correct. - Ajoutez un rappel sur form.submit () pour vérifier si l'email est correct (avec la variable) et empêcher la soumission si ce n'est pas le cas.

De cette façon, vous pouvez conserver un appel asynchrone sans geler l'interface utilisateur du navigateur Web.

-- [modifier] --

Ceci est un code rapide que je viens d'écrire pour l'exemple en fonction de ce que vous avez déjà.

Pour votre information, une notion de programmation appelée "promesses" (futurs et différés en sont d'autres termes) a été inventée pour résoudre exactement le problème que vous avez.

Voici un article sur ce que c'est et comment les utiliser en JavaScript (en utilisant dojo ou jQuery): http://blogs.msdn.com/b/ie/archive/2011/09/11/asynchronous-programming-in -javascript-with-promises.aspx

<script>
  function validateEmailRegistered(input) {
    if (input.setCustomValidity === undefined) return;
    var err = 'Email address not found';
    $.ajax({
      url: 'email-is-registered.php', 
      data: { email: input.value }, 
      success: function(data) {
        var ok = data=='true';
        input.setCustomValidity(ok ? '' : err);
        // If the form was already submited, re-launch the check
        if (form_submitted) {
            input.form.submit();
        }
      }
    });
  }

  var form_submitted = false;

  function submitForm(form) {
    // Save the user clicked on "submit"
    form_submitted = true;
    return checkForm(form);
  }

  function checkForm(form, doSubmit) {
    if (doSubmit) {
        form_submitted = true;
    }
    // If the email is not valid (it can be delayed due to asynchronous call)
    if (!form.email.is_valid) {
        return false;
    }
    return true;
  }
</script>

<form method="get" action="nextpage.php" onsumbit="return submitForm(this);">
  <input name="email" id="email" type="email" required
    onChange="validateEmailRegistered(this)">
  <button type="submit">go</button>
<form>
0
Savageman