Tutorial de IdexedDB

El almacenamiento en el navegador se está convirtiendo en algo fundamental para el desarrollo de aplicaciones web con HTML5 y Javascript. En un primer momento parecía que WebSQL se iba a convertir en el standar, apoyado por Google y Apple en sus navegadores basados en Webkit. Pero la oposición de Microsoft y, sobre todo, de Mozilla sumada al auge de las bases de datos no relacionales en el ámbito del desarrollo móvil, ha hecho que IndexedDB se postule como el nuevo standar para almacenamiento en estos desarrollos. Desde 2010 la W3C ha dado por obsoleto WebSQL.

Todavía no me he puesto a trabajar con él en profundidad, pero os traigo aquí un tutorial basado en este de HTML5Rocks donde se nos explica cómo migrar de WebSQL a IndexedDB.

El ejemplo es una base de datos para un TODO list. Con WebSQL simplemente operábamos como si de una base de datos SQL se tratara, pero el concepto de IdexedDB es distinto. Se trata de un almacén de objetos Javascript donde guardamos parejas de valores: una clave y el objeto. Las búsquedas no se hacen con una query sino que se realiza una consulta sobre un índice que luego es iterado.

El primer paso con IdexedDB es abrir una base de datos:

window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;

// Manejando el prefijo de Chrome para IDBTransaction/IDBKeyRange.
if ('webkitIndexedDB' in window) {
  window.IDBTransaction = window.webkitIDBTransaction;
  window.IDBKeyRange = window.webkitIDBKeyRange;
}

indexedDB.db = null;

// Para depurar enviamos los errores a la consola
indexedDB.onerror = function(e) {
  console.log(e);
};

Arriba ya tenemos abierta una base de datos. Lo segundo, y también fundamental, es crear un almacén de objetos, igual que creamos tablas cuando trabajamos con una base de datos SQL.

indexedDB.open = function() {
  var request = indexedDB.open("todos");

  request.onsuccess = function(e) {
    var v = "2.0 beta"; // puedes poner cadenas en el nombre de la versión
    todoDB.indexedDB.db = e.target.result;
    var db = todoDB.indexedDB.db;
    // En la transacción setVersión es donde podemos crear los almacenes
    if (v!= db.version) {
      var setVrequest = db.setVersion(v);

      // para poder crear almacenes tenemos que hacerlo en onsuccess
      setVrequest.onfailure = todoDB.indexedDB.onerror;
      setVrequest.onsuccess = function(e) {
        if (db.objectStoreNames.contains("todo")) {
          db.deleteObjectStore("todo");
        }

        var store = db.createObjectStore("todo", {keyPath: "timeStamp"});

        var transaction = e.target.result;
        transaction.oncomplete = function() {
          todoDB.indexedDB.getAllTodoItems();
        }
      };
    } else {
      todoDB.indexedDB.getAllTodoItems();
    }
  };

  request.onfailure = todoDB.indexedDB.onerror;
};

Aquí hemos abierto el almacén todos, tras ello comprobamos que la versión de la base de datos sea diferente a la que estamos definiendo. En caso de que no lo sea, con setVersion creamos el nuevo almacén. El único sitio donde podemos modificar la estructura de la base de datos es en setVersion, donde se nos permite crear y eliminar almacenes de objetos.

El siguiente paso es ver cómo podemos añadir objetos a la colección. Para ello creamos la función addTodo.

indexedDB.addTodo = function() {
  var db = todoDB.indexedDB.db;
  var trans = db.transaction(['todo'], 'readwrite');
  var store = trans.objectStore('todo');

  var data = {
    "text": todoText, // el texto debe ser visible aquí
    "timeStamp": new Date().getTime()
  };

  var request = store.put(data);

  request.onsuccess = function(e) {
    todoDB.indexedDB.getAllTodoItems();
  };

  request.onerror = function(e) {
    console.log("Error Adding: ", e);
  };
};

Este método addTodo comienza cogiendo una referencia a la base de datos, seguimos definiendo una transacción readwrite y obtenemos una referencia a la coleccion de objetos. Luego creamos los datos como un objeto JSON y lo metemos en la base con el método put. Guardaremos el texto y un timestamp que servirá como clave única. Definimos que se re-renderizará en caso de éxito y que en caso de error se enviará a la consola.

Y si podemos insertar datos, desde luego podemos también borrarlos. Para ello crearemos el método deleteTodos:

indexedDB.deleteTodo = function(id, text) {
  if (confirm("Are you sure you want to Delete " + text + "?")) {
    var db = todoDB.indexedDB.db;
    var trans = db.transaction(["todo"], 'readwrite');
    var store = trans.objectStore("todo");

    var request = store.delete(id);

    request.onsuccess = function(e) {
      todoDB.indexedDB.getAllTodoItems();
    };

    request.onerror = function(e) {
      console.log("Error Adding: ", e);
    };
  }
};

El funcionamiento es similar al anterior método, pero con un delete en lugar del put, que recibe la clave del objeto para ser borrado.

Finalmente vamos con la lectura e impresioń de datos en pantalla, que a fin de cuentas es lo que nos importa en estos casos. Si queremos almacenar algo es para poder recuperarlo posteriormente.

function showAll() {
  document.getElementById("ourList").innerHTML = "";

  var request = window.indexedDB.open("todos");
  request.onsuccess = function(event) {
    // Enumerar el almacén de objetos completo.
    var db = todoDB.indexedDB.db;
    var trans = db.transaction(["todo"], 'readonly');
    var request = trans.objectStore("todo").openCursor();
    var ul = document.createElement("ul");

    request.onsuccess = function(event) {
      // Esto es un hack para versiones antiguas de Firefox (older versions than 6)
      var cursor = request.result || event.result;

      // Cuando el cursor esté a nulo es que hemos terminado la enumeración, por lo que
      // actualizamos el DOM
      if (!cursor) {
        document.getElementById("ourList").appendChild(ul);
        return;
      }

      var li = document.createElement("div");
      li.textContent = "key: " + cursor.key + " => Todo text: " + cursor.value.text;
      ul.appendChild(li);
      cursor.continue();
    }
  }
}

La idea en el caso de mostrar los resultados es abrir la transacción como readonly, creamos una lista (para este caso) y en cada iteración del cursor creamos un elemento li con un div dentro con los datos que vamos agregando a la lista. Cuando se termine el cursor, en la última iteración, añadimos la lista al DOM.

Como apreciación he de decir que IndexedDB, aunque desde luego es muy útil, tiene cosas que me parecen innecesariamente complicadas. Tal vez sea porque vengo del mundo de las bases de datos SQL pero WebSQL me parecía más claro y usable. Y con todo yo estoy acostumbrado a trabajar con Javascript (reconozco que nunca cosas muy complejas), no quiero imaginarme a muchos programadores que no tocan jamás el lado del cliente.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s