2012-06-16 18 views
13

Tengo una página que hace la validación AJAX de un correo electrónico antes de continuar a la página siguiente, utilizando el método setCustomValidity() de HTML5 [utilizando la biblioteca webshims para navegadores más antiguos].¿Cómo hago una llamada jQuery bloqueando AJAX sin async = false?

Para que esto funcione, establezco la opción asincrónica en falso en la llamada $ .ajax() para hacerla síncrona, bloqueando la página pendiente la respuesta AJAX, ya que de lo contrario el formulario envía antes de que la llamada ajax regrese, representando validación ineficaz.

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

Veo que la opción asíncrona está en desuso a partir de jQuery 1.8.

¿Cómo puedo lograr esta acción de bloqueo sin la opción asincrónica?

+0

¿Qué solución acabas de usar? Estoy enfrentando el mismo problema. –

Respuesta

0

Supongo que hará la validación de forma completa en el envío de todos modos, por lo que tal vez no sea un problema si se interrumpe la validación del correo electrónico y se da prioridad al envío del formulario.

Creo que lo que haría es abortar la solicitud AJAX 'validateEmailRegistered' cuando el usuario envía el formulario. A continuación, realice la validación de formulario completa del lado del servidor como siempre, incluida la validación de correo electrónico.

Mi comprensión de la validación del tipo de tipo es que es una buena forma, no un sustituto para validar el formulario cuando se envía. Entonces no tiene sentido para mí bloquear el envío del formulario.

+0

También estoy haciendo validaciones del lado del servidor, pero eso proporciona una interacción del usuario más pobre que una llamada de bloqueo de AJAX que proporcionó comentarios del usuario a través del mecanismo regular (setCustomValidity). La validación del lado del cliente no es un sustituto de la validación del lado del servidor, pero proporciona una mejor experiencia del usuario. Un mensaje de error del lado del servidor es simplemente otra forma de bloquear, realmente ... – ChrisV

0

Creo que debe cambiar un poco su funcionamiento. No trates de lograr el bloqueo, pero adopta el no bloqueo.

Me gustaría hacer esto: - Mantener la validación en el correo electrónico; hazlo asincrónico Cuando sea válido, establezca una bandera en alguna parte de una variable para saber que está bien. - Agregue una devolución de llamada en form.submit() para verificar si el correo electrónico está bien (con la variable) y evitar el envío si no lo está.

De esta manera puede mantener llamadas asincrónicas sin congelar la IU del navegador web.

- [editar] -

Esto es código rápida que acabo de escribir para el ejemplo sobre la base de lo que ya tiene.

Para su información, una noción de programación llamada "promesas" (futuros y diferidos son otros términos) se ha inventado para resolver exactamente el problema que tiene.

He aquí un artículo sobre lo que es y cómo usarlos en JavaScript (usando dojo o 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

Usar una devolución de llamada en el formulario enviar para evitar el envío es exactamente lo que quiero lograr, pero no veo cómo puedo hacerlo si la devolución de llamada es asincrónico ... – ChrisV

0

Si realmente fijan en no dejar que les envíe el formulario hasta que haya comprobado el validez del correo electrónico, inicie su botón de envío con el atributo disabled, luego haga que se establezca el conjunto de devolución de llamada $('form button').removeAttr('disabled');

Dicho esto, estoy con las otras personas, ¡simplemente déjenlas enviar el formulario! Por lo general, las personas lo hacen bien y se pasan directamente sin errores ...

0

Puede hacerlo de forma asíncrona.

Cree una variable global, la llamo ajax_done_and_successful_flag, que inicializa en falso al comienzo del programa . Configurará esto en verdadero o falso en varios lugares de en sus funciones de Ajax, como la función de éxito Ajax o la función de error de Ajax.

Luego debe agregar un controlador de envío en la parte inferior de su función de validación.

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

El problema es que el código no se está ejecutando de forma lineal.
Ponga un montón de sentencias console.log de Firebug en su código.
Observe la secuencia de ejecución. Verá que su
respuesta Ajax volverá al último, o siempre que lo desee.
Es por eso que necesita submitHandler AND la bandera global
para forzar la función de validación para esperar los resultados correctos de Ajax
antes de enviar el formulario.

Cualquier salida a la pantalla de la respuesta Ajax , hay que hacer en los funciones Ajax, tales como la función de éxito y la función error.
Debe escribir en la misma ubicación como funciones de éxito/error de la función de validación.
De esta forma los mensajes de error Ajax se mezclan con la función de error de la función de validación.
Este concepto puede parecer un poco complicado.
La idea de mantener en cuenta es que las funciones de éxito y error en la función de validación están escribiendo a la misma ubicación que el éxito y error funciones en la llamada Ajax, y que está bien, que es la forma lo que debería ser.
La ubicación de mis mensajes de error está justo al lado de donde el usuario escribe la entrada. Esto crea una buena experiencia de usuario que creo que está solicitando.

Aquí está mi ejemplo de código. Lo simplifiqué.

Me postulo plug-in de validación de jQuery 1.7.1
y jQuery 1.6
estoy usando Firefox 14.0.1 y también me trataron en Chrome 21,0 éxito.

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 

} 

El reg_entry_error es el lugar justo al lado de la entrada de texto. Aquí hay una muestra de código simplificado del formulario.

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

Espero que responda a su pregunta.

1

A partir de jQuery 1.8, el uso de asíncrono: falsa con jqXHR ($ .Deferred) es obsoleto; debe usar las devoluciones de llamada completas/exitosas/de error.

http://api.jquery.com/jQuery.ajax/ (sección asíncrono)

Me pareció un poco molesto ... desarrolladores deberían tener la opción para hacer una llamada de bloqueo con async: false si es algo que la plataforma permite - por qué restringirlo? Acabo de establecer un tiempo de espera para minimizar el impacto de un hang.

Sin embargo, estoy usando una cola ahora en 1.8, que es no bloqueante, y funciona muy bien. Sebastien Roch creó una utilidad fácil de usar que le permite poner en cola las funciones y ejecutarlas/pausarlas. 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(); 

En las 2 primeras funciones que pase el objeto queue en las llamadas, esto es lo que se vería las llamadas como:

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

// similar for ajaxCall2 

Aviso del queue.pause(); al comienzo de cada función, y queue.run() para continuar la ejecución de la cola al final de su declaración complete.

+0

Esto se ve bien, pero el enlace está muerto :( – Gus

+1

gracias por el aviso previo @Gus, busqué en Google y encontré que el proyecto se movió a una cuenta diferente de github. – sonjz

10

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

El uso de la funcionalidad/Promise diferido en peticiones Ajax síncronos ha dejado de utilizarse en 1.8. El método $ .ajax con async: false es compatible pero debe usar un parámetro de devolución de llamada en lugar de un método Promise como .then o .done.

Por lo tanto, si está utilizando los controladores de éxito/completar/error, aún puede usar async:false. Más información en el boleto jquery arriba.

Cuestiones relacionadas