Pregunta

Actualmente no estamos usando ningún marco del lado del cliente serio además de jQuery (y jQuery.ui + validación + complementos de asistente de formulario).

Un problema que surge algunas veces en nuestro código es este:

  1. Tenemos un botón que inicia una llamada Ajax al servidor.
  2. Mientras se realiza la llamada, mostramos un ícono de "carga" con algo de texto
  3. Si el servidor devuelve un resultado demasiado rápido (por ejemplo, <200 ms), "dormimos" durante 200 milisegundos (utilizando setTimeout()), para evitar el parpadeo del icono y el texto en espera .
  4. Después de max(the call returns, a minimal timeout), borramos el icono y el texto de carga.
  5. Luego mostramos un texto de error, si hubo algún problema en la llamada ajax (el servidor no devuelve 500, pero un json personalizado que tiene una propiedad de "mensaje de error". De hecho, a veces tenemos un propiedad en la respuesta por campo de formulario ... y luego hacemos coincidir los errores con los campos de formulario ... pero estoy divagando).
  6. En caso de éxito, hacemos ... algo (depende de la situación).

Estoy tratando de minimizar la reutilización de código y escribir o reutilizar un patrón / pieza de código / marco que haga esto. Si bien probablemente no comenzaré a usar un marco de trabajo pesado completamente nuevo solo para este caso de uso, aún me gustaría saber cuáles son mis opciones ... tal vez ese marco del lado del cliente también sería bueno para otras cosas. Si hay un marco liviano que no requiere que cambie todo mi código al revés, y podría usarlo solo en casos específicos, entonces podríamos usarlo en lugar de reinventar la rueda.

Hace poco escuché sobre Ember.js . ¿Es una buena opción para resolver este problema? ¿Cómo lo resolverías?

¿Fue útil?

Solución

$(function(){
 var buttonSelector = "#button";
 $('body').on({'click': function(evt){
    var $button = $(this);
    $button.toggleClass('loading');
    var time = new Date();
    $.get('some/ajax').then(function(data,text,jqXhr){
   // typical guess at load work
       $button.empty();
       $(data).wrap($button);
    }).fail(function(data,text,jqXhr){
     alert("failed");
    }).done(function(data,text,jqXhr){
       var elapsed = new Date();
      if((elapsed - time) < 200){
        alert("to short, wait");
      }
      $button.toggleClass('loading');
    });
  }},buttonSelector,null);
});

Otros consejos

Just wrap the $.ajax in your own function. that way you can implement your own queing etc. I would suggest to do a jquery component for this. It can get pretty powerful, for example you can also pass http headers etc.
Regarding frameworks it depends on your requirements.
For example, you may consider Kendo UI, it has good framework for creating data sources: http://demos.kendoui.com/web/datasource/index.html.

Working Sample Code (well, almost)

I was going for something along the lines of @DefyGravity's answer anyway - his idea is good, but is still pseudo-code/not fully complete. Here is my working code (almost working demo, up to the Ajax URL itself, and UI tweaks)

The code & usage example:

jQuery.fn.disable = function() {
    $(this).attr("disabled", "disabled");
    $(this).removeClass("enabled");

    // Special handling of jquery-ui buttons: http://stackoverflow.com/questions/3646408/how-can-i-disable-a-button-on-a-jquery-ui-dialog
    $(this).filter("button").button({disabled: true});
};
jQuery.fn.enable = function() {
    $(this).removeAttr("disabled");
    $(this).addClass("enabled");
    // Special handling of jquery-ui buttons: http://stackoverflow.com/questions/3646408/how-can-i-disable-a-button-on-a-jquery-ui-dialog
    $(this).filter("button").button({disabled: false});
};


function AjaxCallbackWaiter(ajaxUrl, button, notificationArea, loadingMessage, errorMessage, inSuccessHandler, inFailureHandler) {
    // Every request that takes less than this, will be intentionally delayed to prevent a flickering effect
    // http://ripper234.com/p/sometimes-a-little-sleep-is-ok/
    var minimalRequestTime = 800;
    var loadingIconUrl = 'http://loadinfo.net/images/preview/11_cyrcle_one_24.gif?1200916238';

    var loadingImageContent = $("<img class='loading-image small' src='" + loadingIconUrl + "'/><span class='loading-text'>" + loadingMessage + "</span>");
    var errorContentTemplate = $("<span class='error ajax-errors'></span>");

    var requestSentTime = null;

    button.click(clickHandler);

    function displayLoadingMessage() {
        clearNotificationArea();
        notificationArea.html(loadingImageContent);
    }

    function clearNotificationArea() {
        notificationArea.html("");
    }

    function displayError(message) {
        var errorContent = errorContentTemplate.clone(errorContentTemplate).html(message);
        notificationArea.html(errorContent);
    }

    function ajaxHandler(result) {
        var requestReceivedTime = new Date().getTime();
        var timeElapsed = requestReceivedTime - requestSentTime;
        // Reset requestSentTime, preparing it for the next request
        requestSentTime = null;

        var sleepTime = Math.max(0, minimalRequestTime - timeElapsed);

        function action() {
            clearNotificationArea();
            button.enable();
            if (result) {
                inSuccessHandler();
            } else {
                displayError(errorMessage);
                inFailureHandler();
            }
        }

        if (sleepTime <= 0) {
            action();
        } else {
            setTimeout(action, sleepTime);
        }
    }

    function failureHandler() {

    }

    function clickHandler(){
        if (requestSentTime !== null) {
            logError("Bad state, expected null");
        }
        requestSentTime = new Date().getTime();
        displayLoadingMessage();
        button.disable();
        $.get(ajaxUrl, 'json').then(ajaxHandler, failureHandler);
    }
}

// Usage:
var ajaxUrl = 'FILL IN YOUR OWN URL HERE';
var button = $("#clickme");
var notificationArea = $(".ajax-notification-area");

var waitingMessage = "Doing Stuff";
var errorMessage = "Not Good<br/> Please try again";

$(document).ready(function(){
  new AjaxCallbackWaiter(
    ajaxUrl,
    button, 
    notificationArea,
    waitingMessage,
    errorMessage,
    function(){
      alert("All is well with the world");
    },
    function(){
      alert("Not good - winter is coming");
    });
});
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top