jueves, 18 de junio de 2015

SlickGrid. DataView (I) - Introducción y uso

Artículos anteriores:
Como comenté en el ejemplo sobre proveedores de datos, el paquete de descarga de SlickGrid incluye una implentación de un proveedor de datos con características muy útiles que no están implementadas en el SlickGrid por defecto, como pueden ser:
  • Paginación
  • Ordenación por múltiples columnas
  • Búsquedas
  • Filtros
  • Agrupaciones de filas y totales
  • Expandir y contraer grupos de filas

Esta implementación de proveedor de datos está encapsulada en el objeto DataView y se encuentra en el archivo slick.dataview.js del paquete de descarga de SlickGrid.

El DataView es un proveedor de datos que se ajusta a la mayoría de los escenarios que nos encontraremos en nuestros desarrollos y, para los casos en los que no se ajuste, puede servirnos como ejemplo o como base para nuestros propios proveedores de datos.

Vamos a ver cómo utilizar el proveedor DataView y sus características.


Configurando un objeto DataView como origen de datos

Lo primero que deberemos hacer para utilizar el DataView es añadir a nuestra página una referencia al archivo slick.dataview.js. Para el ejemplo voy a crearme en el sitio web una nueva página DataView.html con la referencia a éste y el resto de archivos que venimos utilizando, y con la definición ya conocida de otros ejemplos de columnas y opciones del grid.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>DataView</title>
    <link href="https://code.jquery.com/ui/1.11.4/themes/blitzer/jquery-ui.css" rel="stylesheet" />
    <link href="css/slick.grid.css" rel="stylesheet" />
    <link href="css/theme/slick-default-theme.css" rel="stylesheet" />
    <link href="css/pildorasgrid.css" rel="stylesheet" />
    <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="http://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
    <script src="Scripts/jquery.event.drag-2.2.js"></script>
    <script src="Scripts/slick.core.js"></script>
    <script src="Scripts/slick.grid.js"></script>
    <script src="Scripts/gridformatters.js"></script>
    <script src="Scripts/grideditors.js"></script>
    <script src="data/productdatasource.js"></script>
    <script src="Scripts/slick.dataview.js"></script>
    <script type="text/javascript">
        $(function () {
            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: {} }
            ];

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

        });
    </script>
</head>
<body>
    <div id="DataViewGrid" style="width:1024px; height:500px;"></div>
</body>
</html>

Para utilizar el DataView con el SlickGrid debemos:

  • Crear una instancia del objeto DataView. El constructor está accesible a través de Slick.Data.DataView
  • Pasar la instancia del DataView al consructor del SlickGrid como proveedor de datos
  • Enlazar el grid a los eventos onRowCountChanged y onRowsChanged del objeto DataView de forma que el grid se actualicen los datos desde el objeto DataView, cuando cambie el número de filas (onRowCountChanged) o el contenido de determinadas filas (onRowsChanged)
  • Enlazar el DataView al evento onCellChange del grid, de forma que el DataView mantenga el origen de datos actualizado con los cambios realizados en el grid
  • Asignar los datos del origen de datos al DataView. Esto se realiza a través del método setItems del DataView. También está disponible un método getItems que devuelve los datos del array original.

En lo que a los datos se refiere el DataView establece una restricción: los elementos deben tener una propiedad cuyo contenido sea un identificador único del elemento. Por defecto el DataView toma la propiedad id, pero podemos especificar otra diferente pasándole un segundo parámetro al método setItem:

dataView.setItem(datos, [nombrePropiedadId]);

A continuación muestro la implementación de cada uno de estos pasos en la página DataView.html.

var dataProvider = new Slick.Data.DataView();

var grid = new Slick.Grid("#DataViewGrid", dataProvider, columns, options);

dataProvider.onRowCountChanged.subscribe(function (e, args) {
 grid.updateRowCount();
 grid.render();
});

dataProvider.onRowsChanged.subscribe(function (e, args) {
 grid.invalidateRows(args.rows);
 grid.render();
});

grid.onCellChange.subscribe(function (e, args) {
 dataProvider.updateItem(args.item.ProductID, args.item);
});

dataProvider.setItems(products, "ProductID");

Como se puede apreciar he utilizado como origen de datos el array products de ejemplos anteriores, estableciendo la propiedad ProductID como la propiedad a utilizar como identificador de la línea.

En el evento onCellChange del grid comunico al DataView la actualización del elemento utilizando la propiedad ProductID para identificar el elemento a actualizar.

En los eventos onRowCountChanged y onRowsChanged del DataView simplemente fuerzo que el grid se refresque para reflejar los cambios realizados en el DataView.

Si cargamos la página en el navegador comprobaremos que disponemos de la misma funcionalidad que teníamos hasta el momento pero ahora utilizando el DataView como proveedor de datos.

Filas y Elementos

En los métodos de acceso y actualización de datos que expone el DataView puede resultar confuso el índice que se utiliza en algunos de ellos para identificar la fila o el elemento, precisamente por eso: porque en unos casos se trata del índice de la fila o línea del grid y o en otros casos se trata del índice del elemento de datos en el array original.

El proveedor de datos trata de abstraer al grid del origen de datos original por lo que, normalmente, el índice que utiliza es el índice de fila o línea del grid. Sin embargo no siempre es así. Estos son los métodos que el DataView expone para recuperar información:

  • Métodos del interfaz definido para proveedores de datos
    • getItem(row): devuelve el elemento de datos correspondiente a la fila del grid indicada
    • getItemMetadata(row): devuelve información adicional de visualización para la fila del grid indicada
    • getLength(): devuelve el número de filas a mostrar en el grid (no tiene porqué coincidir con el número de elementos en el array original de datos)
  • Métodos propios del DataView
    • getItems(): devuelve el array original de datos. Por tanto getItems()[index] devolverá el elemento de datos correspondiente a la posición index en el array de datos. Esto puede provocar confusión con el método getItem(row) que no utiliza el índice del array, sino el número de fila.
    • getItemById(id): devuelve el elemento de datos con el valor id en la propiedad definida como identificador único.
    • getIdxById(id): devuelve la posición en el array de datos original (no la fila del grid) del elemento con el valor id en la propiedad definida como identificador único.
    • getRowById(id): devuelve la fila del grid que visualiza el elemento con el valor id en la propiedad definida como identificador único.
    • getItemByIdx(index): devuelve el elemento correspondiente a la posición index en el array de datos original. Es equivalente a getItems()[index].

El DataView proporciona también métodos para actualizar datos:

  • deleteItem(id): elimina del array original de datos el elemento con el valor id en la propiedad definida como identificador único
  • addItem(item): añade un elemento al array original de datos
  • insetItem(index, item): inserta el elemento item en la posición index del array de datos original
  • updateItem(id, item): actualiza el elemento con el valor id en la propiedad definida como identificador único con la información contenida en item

Estos métodos provocan que se generen los eventos de actualización de datos del DataView, lo que generaría la actulización automática de los datos en el grid.
Si vamos a realizar un gran número de operaciones de actualización de datos, el DataView generará los eventos para cada operación y el grid realizará el correspondiente "redibujado" tras cada una de ellas. Esto puede representar un serio problema de rendimiento. El DataView nos proporciona dos métodos que solucionan este problema:

  • beginUpdate(): llamar a este método provoca que las operaciones de actualización de datos ejecutadas a partir de esta llamada no generen los eventos onRowsChanged y onRowCountChanged con lo que el grid no se redibuja con cada una de ellas
  • endUpdate(): al llamar a este método provoca que se generen los eventos onRowsChanged y onRowCountChanged pendientes. Únicamente se genera un evento de cada uno, si hemos realizado operaciones de actualización sobre varias filas, en el objeto args del evento onRowsChanged se incluirán todas las filas afectadas.

El código


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



Artículo siguiente:
SlickGrid. DataView (II). Ordenación simple y múltiple

No hay comentarios:

Publicar un comentario