domingo, 14 de junio de 2015

SlickGrid. Carga asíncrona de celdas (y II) - Generando una caché de datos

Artículos anteriores:
Tras ver cómo podemos recuperar la información a mostrar en las celdas de forma asíncrona vamos a ver cómo podemos generar una caché con los datos recuperados de forma que minimicemos las peticiones al servidor y mejoremos la experiencia del usuario al trabajar en nuestra página.

En este ejemplo no utilizo ninguna funcionalidad específica del SlickGrid por lo que se sale un poco del enfoque de tutorial que tienen estos ejemplos, pero me ha parecido interesante incluirlo porque considero que es una carencia del SlickGrid con la que tendrán que lidiar todos aquellos que se decidan a utilizar la carga asíncrona de datos.

Evidentemente la solución que voy a implementar no es la ideal para cualquier caso que se nos pueda presentar, en escenarios complejos seguramente el mejor acercamiento sea el de implementar nuestra propia carga de datos asíncrona obviando la funcionalidad que nos aporta el SlickGrid, sin embargo creo que puede ajustarse a las necesidades de gran número de escenarios sencillos (a parte de servir como ejemplo para otros más complejos).


Definiendo el objeto caché en la columna

Para definir el objeto caché de la columna voy a añadir a la definición de la columna una nueva propiedad cache a la que le asignaré un objeto inicialmente vacío.

var columns = [
 { name: "ID", field: "ProductID", id: "ProductID", width: 60, resizable: false
  , headerCssClass: "prKeyHeadColumn", cssClass: "numericCell", editor: Slick.Editors.Text },
 { name: "Nº Producto", field: "ProductNumber", id: "ProductNumber", width: 120, resizable: false
  , headerCssClass: "headColumn", editor: Slick.Editors.Text },
 { name: "Denominación", field: "Name", id: "Name", width: 250, minWidth: 150, maxWidth: 400
  , headerCssClass: "headColumn", editor: Slick.Editors.Text },
 { name: "Color", field: "Color", id: "Color", width: 80, minWidth: 60, maxWidth: 120
  , headerCssClass: "headColumn", formatter: Slick.Formatters.ColorFormatter
  , editor: Slick.Editors.Color },
 { name: "Precio", field: "StandardCost", id: "StandardCost", width: 110, minWidth: 80, maxWidth: 170
  , headerCssClass: "headColumn", cssClass: "numericCell", formatter: Slick.Formatters.CurrencyFormatter
  , editor: Slick.Editors.Text },
 { name: "Sub", field: "ProductSubcategoryID", id: "ProductSubcategoryID", width: 60, resizable: false
  , headerCssClass: "headColumn", cssClass: "numericCell", editor: subcategoryEditor },
 { name: "Subcategoría", field: "ProductSubcategoryID", id: "SubcategoryName"
  , width:200, minWidth: 150, maxWidth: 400, headerCssClass: "headColumn"
  , formatter: asyncSubcategoryNameFormatter, asyncPostRender: getSubcategoryName, cache: {} }
];

La propiedad cache no es parte de las propiedades definidas en SlickGrid para las columnas, si no una propiedad personalizada definida por mí. En un entorno real deberíamos darle a la propiedad un nombre que nos asegure que no va a generar conflictos con futuras versiones del SlickGrid (el hecho de que ahora mismo el objeto de definición de la columna no tenga una propiedad cache no quiere decir que en futuras versiones no se vaya a implementar, lo que podría ser un quebradero de cabeza adicional si actualizamos la versión), sin embargo para el ejemplo nos sirve y creo que queda más limpio y claro (que siempre es importante en un ejemplo).

A continuación voy a modificar la función getSubcategoryName para que, una vez recuperada la denominación de una subcategoría, introduzca ésta en el objeto cache. Antes de intentar recuperarla comprueba también si ha sido ya recuperada (existe en el objeto cache) y, si es así, directamente la muestra sin necesidad de realizar la petición al servidor.

Voy a modificar también la función de formateo asyncSubcategoryNameFormatter para que compruebe también si existe la subcategoría en el objeto cache y, si es así, muestre la denominación de la caché sin esperar a la llamada a la función asíncrona. En estos casos la función asíncrona getSubcategoryName finalizará sin hacer nada ya que al principio de la función comprobamos que el literal de la celda coincida con el literal "Cargando...".

function asyncSubcategoryNameFormatter(row, cell, value, columnDef, dataContext) {
    if (value == null) return "";

    // Comprobamos si el valor existe en la cache
    if (columnDef.cache[value] !== undefined)
        return columnDef.cache[value];

    return "Cargando...";
}

function getSubcategoryName(cellNode, row, dataContext, colDef) {
    var cell = $(cellNode);
    if (cell.text() !== "Cargando...") return;

    var value = dataContext[colDef.field];
    var name;
    // Comprobamos si el valor existe en la cache
    if (colDef.cache[value] === undefined) {
        name = getSubcatNameFromBBDD(value);
        // Introducimos el valor en la cache
        colDef.cache[value] = name;
    }
    else
        name = colDef.cache[value];

    $(cellNode).text(name);
}

Si volvemos a cargar la página comprobaremos que, si bien el literal "Cargando..." sigue apareciendo al desplazarnos a filas nuevas, la carga de la denominación se realiza más rápidamente y, si volvemos a filas ya visualizadas, ni tan siquiera aparece el literal "Cargando..." mostrándose directamente la denominación de la subcategoría.

Carga con caché

Afinando aún más

Como he comentado anteriormente, aunque un poco por encima, el SlickGrid realiza las llamadas a las funciones definidas en la propiedad asyncPostRender a través de un timeout a intervalos de tiempo regulares para evitar interferir en el funcionamiento de la página o bloquear el navegador.

Ese intervalo de tiempo que espera el SlickGrid entre llamada y llamada es por defecto de 50 milisegundos pero en ocasiones nos puede interesar modificarlo. Dependiendo del escenario podemos querer acelerarlo porque nuestra página es muy ligera o porque resulta crítico que la información se muestre de forma rápida, o ralentizarlo porque , por ejemplo, el proceso que lanza es muy pesado y queremos evitar que bloquee el navegador o que salte mientras el usuario está haciendo scroll en el grid.

La forma en que SlickGrid nos permite definir el intervalo entre las diferentes llamadas es a través de la propiedad asyncPostRenderDelay de las opciones de configuración del grid.

Así podemos modificar nuestra página para que utilice un intervalo más pequeño, por ejemplo de 10 milisegundos.

var options = {
 editable: true,
 autoEdit: false,
 enableAsyncPostRender: true,
 asyncPostRenderDelay: 10
};

Si volvemos a cargar la página comprobaremos que las denominaciones se muestran mucho más rápido reduciendo el tiempo en el que se visualiza el literal "Cargando...".
Se puede ver la diferencia de usar uno u otro valor modificando esta propiedad (por ejemplo estableciendo un valor de 0 ó 500, que equivaldría a medio segundo).

Hay que tener cuidado al establecer el valor de esta propiedad. Debemos tener en cuenta que normalmente es muy diferente un entorno de desarrollo a uno real, y que el usuario no tiene porqué disponer de un equipo igual que el que nosotros manejamos (por ejemplo puede tener grandes limitaciones en cuanto a memoria y procesador si accede a nuestra página con un dispositivo móvil o un equipo antiguo).

El código


Puedes descargar el código de todos los ejemplos de SlickGrid de:



Artículo siguiente:
SlickGrid. Proveedor de datos (I) - Creando un proveedor de datos

No hay comentarios:

Publicar un comentario