Sugerencia rápida:almacene datos en el navegador con IndexedDB

La API de IndexedDB le brinda un almacenamiento rápido de clave/valor en el navegador. Y es compatible con más navegadores de los que crees (gracias a un shim, puede funcionar incluso en los más antiguos). Esto significa que cuando surge la necesidad, tiene una forma de almacenar una colección persistente de datos en el navegador del usuario, sin tener que depender de la conexión a Internet. La mala noticia es que IndexedDB tiene una API muy engorrosa y detallada que es difícil de usar. Afortunadamente, hay una pequeña biblioteca que puede ser de gran ayuda. Se llama db.js y en este consejo rápido le mostraré cómo usarlo.

Cómo usar db.js

IndexedDB se basa en gran medida en la configuración de devoluciones de llamadas, la detección de errores y el trabajo con muchas variables temporales. Db.js oculta esto y expone una interfaz más lineal y fácil de trabajar que cualquier fanático de jQuery apreciaría. La biblioteca incluso usa el mismo mecanismo diferido/promesa que jQuery. Si no sabe lo que significa alguna de las cosas anteriores que mencioné, no se preocupe, quedará más claro una vez que vea algún código.

Para este propósito, vamos a construir una pequeña aplicación de demostración. Va a hacer lo siguiente:

  • Primero, defina el esquema de un nuevo almacén de clave/valor, junto con un número de versión;
  • Intente abrir la base de datos y, si todo salió bien, proceda;
  • Configure un detector de eventos para los clics en el elemento "Agregar". En el cuerpo del oyente, insertaremos un registro en la base de datos y mostraremos el elemento en la página;
  • Por último, cuando se hace clic en un elemento, elimine el registro apropiado de la base de datos y elimínelo de la página.

Además de ser súper rápidas, estas operaciones también son persistentes, de modo que cuando actualice la página o cierre su navegador, los datos seguirán ahí. En una aplicación real, le gustaría sincronizar el contenido de la tienda IndexedDB con su servidor, pero no lo haremos hoy.

El Código

El primer paso es definir el esquema de nuestro almacén de datos. A diferencia de las bases de datos relacionales como MySQL, aquí no tenemos la noción de tablas con columnas y tipos predefinidos. IndexedDB puede contener objetos JavaScript arbitrarios dentro de un único almacén de datos. El único requisito en este paso es elegir opcionalmente un campo de identificación, ya sea que desee que se incremente automáticamente, y definir cero o más índices.

activos/js/script.js

// Use the db.js library, define a schema,
// and listen for events

db.open({
    name: 'database',
    version: 2,
    schema: {
        items: {
            key: {
                keyPath: 'id',
                autoIncrement: true
            },
            indexes: {
                color: { unique: false }
            }
        }
    }
})

Definir un índice le indicará a IndexedDB que debe buscar esa propiedad en los objetos que inserte en el almacén de datos. Entonces podrá recuperar y ordenar todos los objetos que tengan este índice. En el ejemplo anterior, defino un índice en la propiedad de color y le digo a db.js que no quiero que sea único (si lo fuera, solo podría almacenar un objeto con ese color). No estoy usando este índice directamente en la aplicación, pero decidí incluirlo de todos modos para mostrarle cómo se hace. Si desea actualizar el esquema, también deberá incrementar el número de versión.

Como con todo lo demás, abrir una base de datos en IndexedDB es asíncrono y puede fallar en varios casos (el navegador no lo admite, el número de versión es incorrecto o el usuario está en modo de incógnito). Tenemos que pasar una devolución de llamada a done() después de abrir la base de datos, para asegurarnos de que todo fue exitoso. También obtendremos una referencia al objeto del servidor que necesitamos para ejecutar consultas.

db.open({
    name: 'database',
    version: 2,
    schema: {
        items: {
            key: {
                keyPath: 'id',
                autoIncrement: true
            },
            indexes: {
                color: { unique: false }
            }
        }
    }
}).done(function(server){

    // The database was successfully opened. We can
    // run transactions using the server varaible

    // Listen for the document ready event, as we will
    // be working with the dom

    $(function() {

        // Cache some selectors

        var add = $('#add'),
            items = $('#items');

        var colors = ['blue', 'green', 'yellow', 'pink'];

        // On dom.ready, select all items and update the #items ul
        server.items.query().filter().execute().done(function(results){

            if(!results){
                return;
            }

            $.each(results, function(){
                createItem(this);
            });

        });

        // Listen for clicks on the add button
        add.click(function(){

            var item = {
                text: (new Date()).toTimeString(),
                color: colors[ Math.floor( Math.random()*colors.length )]
            };

            server.items.add(item).done(function(){
                createItem(item);
            });

            // If you wish to update an item:
            // server.items.update({id:123, color:'pink', text:'asdf'});
        });

        // When an item is clicked, remove it from the database.
        $('#items').on('click', 'li:not(#add)', function(){
            var item = $(this);
            server.items.remove( item.data('id') ).done(function(){
                item.fadeOut();
            });
        });

        function createItem(item){
            var tmp = $('<li><p></p></li>');

            tmp.addClass( item.color )
                .data('id', item.id)
                .find('p').text( item.text );

            items.prepend(tmp);
        }

    });

}).fail(function(error){

    console.error("An error occured: ", error);

});

Dentro de la devolución de llamada, nosotros:

  • escuche el evento document.ready;
  • seleccionar todos los elementos existentes de la base de datos y mostrarlos en la página;
  • escuche los clics en el elemento "Agregar" y cree nuevos elementos;
  • escuchar los clics en los elementos mismos y eliminarlos de la base de datos;
  • defina una función de devolución de llamada para crear elementos y agregarlos a la página.

En caso de falla, solo registramos el error encontrado en la consola. ¡Con esto, nuestro ejemplo simple de IndexedDB está completo!

Para obtener más información y ejemplos sobre el uso de db.js, consulte esta publicación de blog del desarrollador o lea el código fuente.