Vous êtes sur la page 1sur 14

TEMA 5.

Web storage
Introduccin
Tradicionalmente, la nica forma que tenamos, como desarrolladores, de guardar
informacin en el ordenador del cliente era a travs de cookies. Sin embargo, las
cookies tienen mala fama y algunas limitaciones:

Difciles de gestionar: No podemos leer directamente una cookie, sino que a


travs de document.cookie obtenemos una cadena de todas las cookies y
tenemos que buscar en ella el par nombre=valor que nos interese.

Su nmero y tamao est limitado: Se recomienda que los navegadores


admitan al menos 20 cookies en cada documento de al menos 4Kbytes cada
una, pero esto a veces no es suficiente.

Inseguras: Algunos vectores de ataque se apoyan en el secuestro de cookies


para lograr acceso a servicios que requieren acreditacin.

Por todos estos motivos se han venido desarrollando sistemas alternativos de


almacenamiento local, llamados de forma genrica "Almacenamiento DOM", y gracias
a HTML5 y el xito de los dispositivos mviles para acceder a Internet (que en algunas
ocasiones tienen que seguir funcionando sin conexin), estos sistemas estn avanzando
da a da.
Actualmente existen 3 tcnicas para almacenar informacin local, que pese a estar an
en fase de desarrollo, se encuentran ya implementadas en mayor o menor medida en la
mayora de los navegadores:

Almacenamiento local, con esta tcnica los datos son permanentes y se pueden
compartir entre todos los documentos procedentes de mismo origen
(protocolo+dominio+puerto)

Almacenamiento de sesin, los datos almacenados con esta tcnica se borran


automticamente al cerrar la ventana del navegador y slo pueden ser
compartidos por los documentos cargados en la misma ventana (es decir, por el
documento principal y los elementos iFrame anidados en l).

IndexedDB, tcnica an ms flexible que la anterior pues, por ejemplo, permite


definir varios "almacenes" (en este caso se denominan objetos de
almacenamiento), crear ndices sobre esos almacenes para poder localizar los
datos filtrndolos por diversos criterios, y estn pensadas para almacenar
grandes cantidades de informacin (generalmente ms de 50 Mbytes).

Al igual que las cookies, las tcnicas de almacenamiento Local y de Sesin:

almacenan pares nombre/valor, pero se puede acceder a ellos de


forma individual,

pueden ocupar como mnimo hasta 5Mbytes, y

su seguridad es mayor porque no se envan con las peticiones.


1

Nota: Los datos almacenados en el cliente son visibles para ste; por ejemplo, en Firebug puede verlos a
travs del panel DOM desactivando la opcin Mostrar slo propiedades propias (si estuviera
activada).

El almacn local est disponible a travs de la propiedad window.localStorage,


mientras que el de sesin est disponible en window.sessionStorage. Estas
propiedades contienen un objeto de tipo Storage, cuyas propiedades y mtodos se
describen a continuacin.

El objeto Storage
La nica propiedad de este objeto es length, que indica cuntos pares nombre/valor
hay en el almacen.
Podemos acceder a los elementos del almacn mediante la sintaxis de corchetes
indicando el nombre del par correspondiente (no el ndice). No obstante, tambin
disponemos de los siguientes mtodos:

getItem(nombre): Devuelve el valor del par identificado por nombre.

setItem(nombre,valor): Crea, o modifica si ya existiera, el par especificado por


nombre y valor.

removeItem(nombre): Borra del almacn el par identificado por nombre.

key(ndice): Contiene el nombre del par que ocupa la posicin ndice dentro del
almacn.

clear(): Borra todos los pares del almacn.

El evento storage
Cada vez que se produce una modificacin en un almacn se genera un evento de tipo
storage, que en el caso del almacenamiento local se comunica a todas las ventanas
del navegador que contienen documentos procedentes de ese mismo origen, y que en el
caso del almacenamiento de sesin se comunica slo a los documentos de ese origen
cargados en la misma ventana que el que provoc el cambio.
Este evento se asigna al objeto window, y su funcin oyente recibe como argumento un
objeto de tipo Event ampliado con las siguientes propiedades:

key: Es el nombre del par al que ha afectado el cambio.

oldValue: Es el valor que tena el par antes de la modificacin.

newValue: Es el nuevo valor del par despus de la modificacin.

storageArea: Es una referencia al objeto de tipo Storage que ha sido modificado


(el local o el de sesin).

Al vaciar un almacn con clear() se genera un nico evento storage en lugar de


uno por cada par que se haya eliminado.
Siga estos pasos para probar el funcionamiento del almacenamiento DOM:
1. Vamos a crear un documento llamado supervisor.html en el que cada evento de
tipo storage aada un prrafo nuevo con las propiedades del objeto de evento
recibido. Su cdigo sera el siguiente:
supervisor.html
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML5</title>
<!--<link rel="stylesheet" href="css/main.css" type="text/css">-->
<style>
</style>
<!--<script src='jquery-1.7.2.min.js'></script>-->
<script>
window.addEventListener('storage',mostrar);
function mostrar(evento){
var mensaje = '<p><strong>Nombre:</strong>
'+evento.key;
mensaje += '<strong> Valor actual:</strong>
'+evento.newValue;
mensaje += '<strong> Valor anterior:</strong>
'+evento.newValue;
if(evento.storageArea == window.localStorage){
mensaje += '<strong> Almac&eacute;n:</strong>
local</p>';
}else{
mensaje += '<strong> Almac&eacute;n:</strong>
sesi&oacute;n</p>';
}
document.body.insertAdjacentHTML('beforeend',mensaje);
}
</script>
</head>
<body>
</body>
</html>

2. Ahora vamos a crear otro documento llamado indexLocal.html que permita


aadir pares al almacen local y borrarlo completamente. Su cdigo sera el
siguiente:
indexLocal.html
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML5</title>
<!--<link rel="stylesheet" href="css/main.css" type="text/css">-->
<style>
</style>
<!--<script src='jquery-1.7.2.min.js'></script>-->
<script>
function almacenar(){
var nombre=document.getElementById('nombre').value;
var valor=document.getElementById('valor').value;
window.localStorage.setItem(nombre,valor);
insertar(nombre,valor);
}
function insertar(nombre,valor){
var mensaje = '<p><strong>Nombre:</strong> '+ nombre;
mensaje += '<strong> Valor actual:</strong> '+ valor +'</p>';

020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044

document.body.insertAdjacentHTML('beforeend',mensaje);
}
document.addEventListener('readystatechange',cargarDatos);
function cargarDatos(evento){
if(document.readyState == 'complete'){
var i;
for(i=0;i<window.localStorage.length;i++){
insertar(window.localStorage.key(i),window.localStorage[window.localStorag
e.key(i)]);
}
}
}
</script>
</head>
<body>
<label for='nombre'>Nombre: </label>
<input type='text' id='nombre' />
<br />
<label for='valor'>Valor: </label>
<input type='text' id='valor' />
<br />
<button onclick='almacenar();'>Almacenar</button>
<br />
<button
onclick='window.localStorage.clear();insertar("BORRADO","TOTAL");'>Borrar
almac&eacute;n</button>
</body>
</html>

1. Abra cada uno de los documentos anteriores en una pestaa del navegador y
compruebe que al introducir pares en indexLocal.html o borrar el almacn, los
cambios se reflejan en supervisor.html.
2. Cree varios pares, cierre estas dos pestaas de navegacin y vuelva a abrir otra
vez indexLocal.html. Comprobar que recupera los pares del almacn y los
muestra debajo del formulario.
3. A continuacin vamos a crear otro documento llamado indexSesion.html que
realice la misma operacin pero sobre el almacn de sesin y que contenga un
iFrame asociado al documento supervisor.html. Su cdigo sera el siguiente:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML5</title>
<!--<link rel="stylesheet" href="css/main.css" type="text/css">-->
<style>
</style>
<!--<script src='jquery-1.7.2.min.js'></script>-->
<script>
function almacenar(){
var nombre=document.getElementById('nombre').value;
var valor=document.getElementById('valor').value;
window.sessionStorage.setItem(nombre,valor);
insertar(nombre,valor);
}
function insertar(nombre,valor){
var mensaje = '<p><strong>Nombre:</strong> '+ nombre;
mensaje += '<strong> Valor actual:</strong> '+ valor +'</p>';
document.body.insertAdjacentHTML('beforeend',mensaje);
}
document.addEventListener('readystatechange',cargarDatos);
function cargarDatos(evento){

024
025
026
027

if(document.readyState == 'complete'){
var i;
for(i=0;i<window.sessionStorage.length;i++){

insertar(window.sessionStorage.key(i),window.sessionStorage[window.session
Storage.key(i)]);
}
}
}
</script>
</head>
<body>
<label for='nombre'>Nombre: </label>
<input type='text' id='nombre' />
<br />
<label for='valor'>Valor: </label>
<input type='text' id='valor' />
<br />
<button onclick='almacenar();'>Almacenar</button>
<br />
<button
onclick='window.sessionStorage.clear();insertar("BORRADO","TOTAL");'>Borrar
almac&eacute;n</button>
<br />
<iframe width="100%" src="supervisor.html"></iframe>
</body>
</html>

028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046

3. Abra el archivo indexSesion.html en una pestaa del navegador mientras


mantiene en otra el archivo supervisor.html.
4. Compruebe que al insertar pares en indexSesion.html se actualizan en el
supervisor.html del iFrame porque se encuentra en su misma ventana, pero no en
el de la otra ventana.
5. Compruebe tambin que si cierra la ventana de indexSesion.html y la vuelve a
abrir no se recuperan los elementos del almacn de sesin, pues se borran
automticamente al cerrar la sesin de navegador, a diferencia de los locales,
que permanecen hasta que se borran expresamente.
6. Pruebe este mismo ejercicio con varios navegadores.

IndexedDB
Actualmente existen dos APIs dentro de HTML5 para manejar bases de datos en el lado
del cliente:

Web SQL: El desarrollo de esta API ha sido abandonado por el W3C, por lo
que su uso est desaconsejado. Sin embargo, algunos navegadores (como
Chrome, Safari, Opera y los navegadores para mviles) an lo soportan en sus
ltimas versiones. El motivo del abandono fue que Web SQL se basa
exclusivamente en SQLite, y esta aplicacin utiliza una variante del
lenguaje SQL que no cumple exactamente su estndar.

IndexedDB: Es la API recomendada actualmente para suar bases de datos en el


lado del cliente. Est soportada por Chrome, Firefox, Opera e Internet Explorer,
pero no por Safari ni por ninguno de los navegadores para mviles.

Las principales caractersticas de las bases de datos IndexedDB son:

Principalmente asncrona: Esta tecnologa est diseada para poder trabajar de


modo sncrono (detener el flujo de ejecucin hasta que se obtenga el resultado
de una operacin) o asncrono, pero actualmente slo est implementado el
modo asncrono. En el modo asncrono, al realizar una peticin no se obtiene la
respuesta inmediatamente, sino que se genera un evento (generalmente de tipo
success o error) cuando la respuesta est disponible y entretanto sigue el flujo de
ejecucin.

Almacenan pares clave-valor: Los valores pueden ser objetos, y adems se


puede definir ndices que permitan acceder a los pares por cualquiera de las
propiedades de esos objetos en lugar de exclusivamente a travs de la clave.

Orientada a objetos: Las bases de datos ms populares, como Access o


MySQL, son relacionales; es decir, contienen tablas que, a su vez, estn
compuestas por registros (filas), y que a su vez contienen campos (celdas). Por
el contrario las bases de datos IndexedDB estn orientadas a objetos y esto
quiere decir que estn compuestas por objetos de un tipo denominado
IDBObjectStore en lugar de tablas. Dentro de estos objetos se almacenan los
pares clave-valor (recuerde que el valor suele ser a su vez un objeto).

Transaccionales: Cualquier operacin que queramos realizar sobre la base de


datos deber iniciarse dentro de una transaccin, que es un contexto que
habremos definido previamente indicando sobre qu objetos IDBObjectStore
queremos trabajar y en qu modo (slo lectura, lectura y escritura, ).

Admiten versiones: Al abrir una base de datos IndexedDB podemos indicar su


versin a travs de un nmero entero, de modo que si en el ordenador del
usuario existe una versin ms antigua se generar un evento upgradeneeded que
nos permitir configurar en su equipo la estructura de la nueva versin de la base
de datos. Esto resulta muy til cuando desarrollamos nuevas versiones de una
aplicacin web y queremos mantener la estructura de la base de datos del cliente
actualizada.

ndices: Adems de poder acceder a los pares clave-valor de los objetos


IDBObjectStore a travs de su clave, tambin podremos definir otros ndices que
nos permitan filtrarlos por cualquiera de las propiedades de su objeto valor. Por
ejemplo, si un IDBObjectStore contiene informacin personal sobre los alumnos
de un centro escolar, la clave podra ser un id nico (como el DNI o el nmero
de expediente), y el valor podra ser un objeto con propiedades como nombre,
apellidos, telfono, En este escenario podramos, por ejemplo, definir un
ndice sobre la propiedad nombre que nos permitiese localizar a los alumnos por
este dato.

Cursores: Podemos acceder simultneamente a varios pares clave-valor, bien


directamente de un IDBObjectStore o bien de un ndice definido sobre l,
abriendo un cursor en el que definiremos un rango sobre la clave o la propiedad
del ndice (por ejemplo, todos los alumnos con nmeros de expedientes entre el
2000 y el 3000).

Poltica de mismo origen: Las bases de datos creadas por un origen


(protocolo+dominio+puerto) slo son accesibles para los documentos servidos
desde ese mismo origen. En otras palabras, un pgina de piratas.com no podr
acceder a una base de datos creada en nuestro sistema por amazon.com.
6

El procedimiento general para utilizar una base de datos IndexedDB consiste en:
1. Abrir la base de datos.
2. Si es la primera vez que abrimos la base de datos o es una versin nueva
tendremos que definir los objetos IDBObjectStore y los ndices que conforman
su estructura. Estos elementos no se podrn modificar a posteriori a menos, claro
est, que creemos una versin nueva de la base de datos.
3. Definir una transaccin para indicar sobre qu objetos IDBObjectStore
queremos actuar y cmo deseamos hacerlo (slo lectura, lectura y escritura, ).
4. Elegir el IDBObjectStore de la transaccin sobre el que queremos realizar una
peticin.
5. Realizar peticiones sobre el IDBObjectStore identificado en la transaccin, o
bien sobre cualquiera de sus ndices, o bien sobre sus cursores (que pueden
crearse a posteriori, tanto directamente sobre los propios IDBObjectStore como
sobre sus ndices).

Abrir una base de datos IndexedDB


El procedimiento para abrir una base de datos consiste en realizar una peticin de tipo
open sobre el objeto window.indexedDB indicando el nombre de la base de datos y
la versin. Por ejemplo:
var peticin = window.indexedDB.open('alumnos',2);

La primera vez que intentemos abrir una base de datos se solicitar permiso al usuario y,
si no nos lo concede, se generar un evento de tipo error.
La versin debe ser un nmero entero. Si intentamos abrir una versin ms antigua que
la disponible en el ordenador del usuario se generarn un evento de tipo error y un
error de tipo VersionError; por el contrario, si es la primera vez que abrimos la
base de datos o queremos abrir una versin ms reciente que la disponible en el
ordenador del usuario se generar un evento de tipo upgradeneeded. Tras abrir la
base de datos se generar un evento de tipo success.
En el oyente de los eventos upgradeneeded y success, la referencia a la base de
datos est disponible a travs de la propiedad result del blanco del evento, es decir,
en evento.target.result.
Por ejemplo:
001
002
003
004
005
006
007

function abrirBaseDeDatos() {
var peticion = indexedDB.open('Notas');
peticion.addEventListener('upgradeneeded', actualizarBaseDeDatos);
peticion.addEventListener('error', function(){console.log('Error al abrir BD');});
peticion.addEventListener('success', exitoAbrir);
}
function exitoAbrir(evento) {

008
009

baseDeDatos = evento.target.result;
}

Nota: El objeto base de datos (IDBDatabase) posee la propiedad version, que nos indica cul es la
versin de la base de datos que hemos abierto. Si no indicamos una versin en el mtodo open se
abrir la ms reciente disponible en el ordenador del cliente y, si no existe ninguna, se crear con
el nmero de versin 1.

Otro mtodo importante del objeto indexedDB es deleteDatabase, que sirve para
solicitar que se borre una base de datos. Si esta solicitud consigue realizarse
correctamente se generar un evento success sobre ella. Para poder borrar una base
de datos es necesario que no tengamos ningna conexin con ella abierta (el mtodo
close() aplicado sobre una conexin a una base de datos sirve para cerrarla).
Por ejemplo:
001
002
003
004
005
006
007
008
009
010
011
012

function borrarBaseDeDatos() {
if(baseDeDatos != null){
//Para poder borrar debe estar cerrada
baseDeDatos.close();
}
var peticion = indexedDB.deleteDatabase('Notas');
peticion.addEventListener('success', exitoBorrar);
peticion.addEventListener('error', function(){console.log('Error borrando
BD');});
}
function exitoBorrar(){
console.log('Base de datos borrada');
}

Definir la estructura de la base de datos


La estructura de una base de datos est compuesta por la configuracin de sus objetos
IDBObjectStore y la de los ndices que queramos usar sobre ellos. Esta configuracin
slo puede establecerse en el oyente del evento upgradeneeded.
Para configurar un IDBObjectStore tendremos que llamar al mtodo
createObjetoStore sobre la referencia de la base de datos (recuerde que est
disponible en la propiedad result del blanco del evento) indicando su nombre, y un
objeto con la configuracin de la clave. La clave de un IDBObjectStore puede ser de
dos tipos: en lnea o fuera de lnea. Las claves en lnea son aqullas que corresponden a
una de las propiedades del valor (es decir, la clave se almacena formando parte del valor
del par). Por ejemplo, si los valores que vamos a almacenar tienen la forma {dni: "xxx",
nombre: "yyy"} podramos usar como clave en lnea dni. Por el contrario, las claves
fuera de lnea son aqullas que no corresponden a ninguna propiedad. Por ejemplo, si
los objetos que vamos a almacenar tienen la forma {nombre: "xxx", profesion: "yyy"}
tendramos que usar una clave fuera de lnea pues ninguno de estos valores es nico.
Para usar una clave en lnea deberamos pasar a createObjetStore como segundo
argumento un objeto con el nombre de la propiedad que queramos utilizar como clave
asignada a la propieda keyPath. Adicionalemente, si lo deseamos podremos
configurar esa clave para que se auto-incremente con cada nuevo par que aadamos al
objeto incluyendo en este segundo argumento la propiedad autoIncrement con el

valor true). Si no especificamos el segundo argumento se utilizar una clave fuera de


lnea gestionada por el navegador. Este mtodo devuelve una referencia al objeto
IDBObjectStore creado. Por ejemplo:
var
objetoStore=baseDeDatos.createObjectStore('nota',{keyPath:
true});

"id",

autoIncrement:

La clave no tiene por qu ser numrica, pero s necesariamente nica para cada par (no
est permitido crear en un mismo IDBObjectStore dos pares con la misma clave). Si nos
interesa, tambin podemos indicar como clave cualquiera de las propiedades de los
objetos que queramos almacenar en el IDBObjectStore (siempre que sea nica para cada
objeto); por ejemplo, si disponemos de objetos con las propiedades dni, nombre y
telfono se podra elegir dni como valor de la propiedad keyPath.
La creacin de ndices se realiza a travs del mtodo createIndex de los
IDBObjectStore, que recibe 3 argumentos: el nombre que queremos asignar al ndice, el
nombre de la propiedad que deseamos usar como criterio del ndice, y un objeto en el
que se configuran las propiedades del ndice (por ejemplo, si debe ser nico (unique),
es decir, que no se admitan en el IDBObjectStore dos pares que compartan el mismo
valor en la propiedad declarada como criterio del ndice). Por ejemplo:
objetoStore.createIndex('titulo','titulo', {unique: false});

Otra propiedad muy til de los ndices es multiEntry; imagine que elegimos como
criterio del ndice una propiedad que es de tipo array, si configuramos multiEntry
como false se crear en el ndice una sola entrada para cada par, pero si lo
configuramos como true se crear una entrada para cada valor del array de cada par.
Al abrir una versin de la base de datos ms reciente que la disponible en el ordenador
del usuario cliente ya sabemos que se genera un evento upgradeneeded, pero es
esencial entender que en la nueva versin se importarn los IDBObjectStore e ndices
que pudiera contener la versin anterior, de modo que si deseamos introducir
modificaciones en ellos tendremos que borrarlos y volver a crearlos (en el oyente del
evento upgradeneeded), pero PRECAUCIN!: al borrar un IDBObjectStore se
eliminan tambin todos los pares que contiente. Los mtodos que permiten borrar
IDBObjectStores e ndices son:

deleteObjectStore(nombreDelIdbObjectStore): Este mtodo del objeto base de


datos (recuerde que su referencia se encuentra en la propiedad result del
blanco de los evento success y upgradeneeded) borra el IDBObjectStore
(incluidos todos sus pares y todos sus ndices).

deleteIndex(nombreDelIndice): Este mtodo se ejecuta sobre el objeto


IDBObjectStore y elimina el ndice (sin afectar a los pares que contenga el
IDBObjectStore). Pero cmo se accede a un objeto IDBObjectStore? Ya
sabemos que el mtodo createObjectStore devuelve una referencia al
objeto IDBObjectStore recin creado, pero en ese momento no tiene sentido
eliminar un ndice porque no existe ninguno. Realmente el escenario en el que
9

puede interesarnos borrar un ndice es cuando cambiamos de versin y queremos


mantener un IDBobjectStore de la versin anterior pero no alguno de sus
ndices; en este caso para poder acceder al IDBObjectStore necesitaremos
definir una transaccin y, dentro de ella, recurrir a su mtodo
objectStore() para obtener la referencia del IDBObjectStore por su
nombre. En la siguiente seccin se explica con detalle el uso de transacciones.

Transacciones
Una transaccin es un conjunto de operaciones (peticiones o IDBRequest) de acceso o
modificacin de datos con las siguientes caractersticas:

Alcance: Al definir una transaccin deberemos indicar sobre qu


IDBObjectStores podrn actuar sus operaciones y si lo harn en modo de slo
lectura, o de lectura y escritura. Podemos tener varias transacciones activas a la
vez siempre que sus alcances en modo de escritura no se solapen; en otras
palabras, si hemos definido una transaccin en modo de lectura y escritura sobre
un IDBObjectStore no podremos definir otra transaccin en modo de lectura y
escritura que afecte a ese mismo IDBObjectStore. Esta limitracin no afecta a
las transacciones en modo de slo lectura.

Reversibilidad: En una misma transaccin pueden llevarse a cabo varias


operaciones una detrs de otras (iniciando la siguiente en el oyente del evento
success de la anterior), pero en cualquier momento podemos llamar al mtodo
abort() de la transaccin para deshacer todas las operaciones realizadas. Por
ejemplo, imagine que en el conjunto de peticiones concatenadas de una
transaccin una de ellas produce un error; esto provocara que la transaccin
completa se abortase deshaciendse todas las peticiones previas. Pero tambin
podra interesarnos llamar al mtodo abort() manualmente si el resultado de
alguna de esas peticiones no fuera el esperado. Pero cmo concatenamos
peticiones dentro de una transaccin? Mediante los oyentes de evento success;
en estos oyentes, dentro de evento.target.transaction dispondremos
de una referencia a la transaccin y podremos elegir un objectStore y una nueva
peticin.

Nota: Si una transaccin readwrite contiene varias peticiones concatenadas, los cambios no se
introducirn efectivamente en el objectStore hasta que se llegue a un oyente de evento success en
el que no se lance otra nueva peticin. En otras palabras: los cambios no se producen hasta que
no se culminan todas las peticiones de la transaccin.

Nota: Los eventos success no se propagan, pero los error s, pasando de la peticin, a la transaccin
y, por ltimo, al propio objeto de base de datos. Esto puede resultar muy til para definir un nico
oyente de evento error asociado a la base de datos y no tener que hacerlo individualmente en
cada peticin.

Para declarar una transaccin debemos utilizar el mtodo transaction() del objeto
base de datos, al que pasaremos como argumentos un array con los nombres de los
objetos IDBObjectStore sobre los que queremos actuar (o sencillamente una cadena con
10

el nombre en caso de que queramos actuar slo sobre uno), y una cadena indicando el
modo ('readonly' o 'readwrite'). Por ejemplo:
var transaccion = baseDeDatos.transaction(['nota'],'readonly');

La transaccin posee el mtodo objectStore() que nos permite indicar sobre qu


IDBObjectStore de los declarados en la transaccin queremos actuar. Por ejemplo:
var objetoStore = transaccion.objectStore('nota');

Peticiones
Una vez declarada la transaccin y elegido el IDBObjectStore sobre el que queremos
actuar podremos realizar peticiones de lectura y escritura sobre el propio
IDBObjectStore, o slo de lectura sobre sus ndices, o abrir cursores en cualquiera de
ellos para leer rangos de pares (no escribirlos). Estas peticiones se realizarn de forma
asncrona, de modo que tendremos que asignarles oyentes de eventos success y/o
error
para
conocer
su
resultado
(a
travs
de
la
propiedad
evento.target.result).
El objeto IDBObjectStore nos ofrece los siguientes mtodos para realizar peticiones
sobre l:

001
002
003
004

add(valor,[clave]): .Aade valor al IDBObjectStore. Si el objeto valor contiene


una propiedad cuyo nombre conincide con el de la propiedad keyPath
especificada al crear el IDBObjectStore se utilizar el valor de esa propiedad
como clave. Si el IDBObjectStore fue declarado con una clave auto-incremental
no har falta indicar la clave. Opcionalmente podemos indicar expresamente la
clave del par con el segundo argumento. Tenga en cuenta que este mtodo slo
sirve para aadir nuevos pares; no para modificar los existentes (si intentamos
aadir un par con la misma clave que otro ya existente en el IDBObjectStore se
producir un error. En la propiedad result de la peticin obtendremos la clave
que se ha asignado al par (esto es muy til en caso de no indicar expresamente la
clave). Por ejemplo:
var transaccion = baseDeDatos.transaction(['nota'],'readwrite')
var objetoStore = transaccion.objectStore('nota')
var peticion = objetoStore.add({titulo: 'Revisar', contenido: 'API acceso BD'})
peticion.addEventListener('success',exitoGuardar)

clear(): Inicia una peticin que elimina todos los pares del IDBObjectStore y
todos sus ndices.

count(claveOrango): Inicia una peticin que cuenta el nmero de pares del


IDBObjectStore cuya clave concuerda con la indicada en el argumento o est
dentro del rango. En la seccin posterior "Rangos y cursores" se explica cmo
11

crear rangos de claves. Al concluir con xito la peticin podremos saber cuntos
pares son a travs de la propiedad result de la peticin
(evento.target.result).

delete(claveOrango): Inicia una peticin que borra del IDBObjectStore todos


los pares cuya clave concuerda con la especificada en el argumento o est dentro
del rango.

get(claveOrango): Inicia una peticin que obtiene del IDBObjectStore el valor


del par cuya clave concuerda con la especificada en el argumento, o del primero
cuya clave pertence al rango. Este valor est disponible en la propiedad result
de la peticin.

put(valor,[clave]): Este mtodo, como add(), sirve para aadir pares al


IDBObjectStores, pero si la clave indicada corresponde a un par ya existente
sobrescribe su valor. Si el IDBObjectStore utiliza una clave en lnea, la
propiedad correspondiente deber indicarse en el valor. En la propiedad
result de la peticin obtendremos la clave que se ha asignado al par.

ndices
Los ndices se crean sobre los IDBObjectStores para facilitarnos el acceso (slo lectura)
a los pares usando como criterio una propiedad distinta a su clave. Recuerde que los
ndices slo se pueden crear en el oyente del evento upgradeneeded.
Para hacer uso de un ndice tendremos que obtener una referencia a l a travs del
mtodo index(nombreDelIndice) del objeto IDBObjectStore (recuerde que la
referencia al objeto IDBObjectStore la obtenemos a su vez a travs del mtodo
objectStore(nombre) de la transaccin.
A travs de la referencia del ndice podremos ejecutar sus mtodos get() y count(),
que funcionan exactamente igual que en el objeto IDBObjectStore explicado en la
seccin anterior.

Rangos y cursores
Un rango es el conjunto de pares de un IDBObjectStores cuyas claves se encuentran
comprendidas dentro del intervalo que definamos, y un cursor es un objeto que nos
permite recorrer los pares que componen el rango.
Los rangos pueden ser de 4 tipos (coincidencia exacta, mayor que, menor que, o
comprendido entre) y se crean mediante los siguientes mtodos del objeto
IDBKeyRange:

IDBKeyRange.only(clave): Crea un rango de coincidencia exacta, en el que los


pares debern tener una clave exacta a la indicada para ser incluidos en el cursor
(o cualquier otra peticin de tipo get, delete o count).

IDBKeyRange.lowerBound(clave,[abierto]): El argumento booleano opcional


abierto indica si queremos que el lmite inferior del rango sea abierto o cerrado
(por defecto adquiere el valor false, es decir, es cerrado). Este mtodo crea un
12

rango de tipo mayor que, en el que los pares debern tener una clave mayor si el
lmite es abierto, o mayor o igual que si el lmite es cerrado, que la especificada
para ser incluidos en el cursor.

IDBKeyRange.upperBound(clave,[abierto]): El argumento booleano opcional


abierto indica si queremos que el lmite superior del rango sea abierto o cerrado
(por defecto adquiere el valor false, es decir, es cerrado). Este mtodo crea un
rango de tipo menor que, en el que los pares debern tener una clave menor si el
lmite es abierto, o menor o igual que si el lmite es cerrado, que la especificada
para ser incluidos en el cursor.

IDBKeyRange.bound(claveInferior,
claveSuperior,[abiertoInferior],
[abiertoSuperior]): Define un rango de tipo comprendido entre los valores
claveInferior y claveSuperior.

Tanto los objetos IDBObjectStore como sus ndices nos ofrecen el mtodo
openCursor(rango,[direccin]), que inicia una peticin para acceder (leer,
escribir y borrar) al primer par cuya clave pertenezca al rango que especifiquemos, es
decir, colocar el cursor sobre l.
Pese a que openCursor() simplemente coloca el puntero sobre el primer par, los
cursores estn diseados para facilitar la iteracin sobre conjuntos de pares. Cualquier
operacin que realicemos sobre el cursor (sus mtodos se explican a continuacin)
generar un evento (success o error) sobre la peticin. En otras palabras: los
oyentes de eventos success y error de la peticin devuelta por openCursor()
servirn tambin para atender las operaciones que realicemos sobre el cursor.
Si el cursor se aplica a un IDBObjectStore, el rango deber estar definido sobre la clave
principal, es decir, sobre el KeyPath; por el contrario, si el cursor se aplica sobre un
ndice, el rango deber estar definido sobre el criterio (o clave local) de ese ndice.
El argumento direccin del mtodo openCursor() admite los siguientes valores:

"next": Coloca el cursor en el par del rango cuya clave es menor e itera en
sentido creciente de par en par.

"nextunique": Igual que el anterior, pero al avanzar en sentido creciente lo


hace buscando aqul cuya clave es diferente a la del seleccionado actualmente;
en otras palabras: de los pares que comparten la misma clave slo itera sobre el
que tiene la clave menor.

"prev": Coloca el cursor en el par del rango cuya clave es mayor e itera en
sentido decreciente de par en par.

"prevunique": Igual que el anterior, pero al avanzar en sentido decreciente lo


hace buscando aqul cuya clave es diferente a la del seleccionado actualmente;
en otras palabras: de los pares que comparten la misma clave slo itera sobre el
que tiene la clave mayor.
Por ejemplo:

13

001
002
003
004

var
var
var
var

transaccion = baseDeDatos.transaction(['nota'],'readonly');
objetoStore = transaccion.objectStore('nota');
rango=IDBKeyRange.bound('Desde','Hasta',false,false);
peticion = objetoStore.index('titulo').openCursor(rango,'nextunique');

El resultado (result) de la peticin (es decir, el cursor) posee las siguientes


propiedades y mtodos:

key: Contiene la clave local del par sobre el que se encutra actualmente el
cursor. Si el cursor se ha abierto sobre un IDBObjectStore, la clave local
coincidir con la principal, es decir, con la configurada en el keyPath.

primaryKey: Contiene la clave principal de par sobre el que se encuentra


actualmente el cursor.

update(valor): Inicia una peticin que almacena sobre el par en el que se


encuentra actualmente el cursor el valor indicado. Si el IDBObjectStore usa una
clave en lnea, la propiedad de la clave deber especificarse en el valor. Si
queremos continuar iterando sobre el rango tendremos que recurrir a cualquiera
de los dos mtodos que se describen a continuacin (advance o continue).
Si ejecutamos este mtodo encontrndose el cursor sobre un par que acaba de ser
borrado con el mtodo delete() (explicado posteriormente), se crear un par
nuevo.

advance(posiciones): Hace avanzar al cursor el nmero de posiciones indicado


dentro del rango.

continue([clave]): Si no se especifica una clave hace avanzar el cursor al


siguiente par del rango segn la direccin especificada. Si se especifica una
clave hace avanzar el cursor hasta el siguiente par cuya clave concuerda con la
especificada.

delete(): Inicia una peticin que elimina el par sobre el que se encuentra el
cursor. El cursor no cambiar de posicin a menos que a continuacin
ejecutemos el mtodo continue o advance.

Por ejemplo:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015

var transaccion = baseDeDatos.transaction(['nota'],'readwrite');


var objetoStore = transaccion.objectStore('nota');
var
rango=IDBKeyRange.bound(parseInt(campoDesdeId.value),parseInt(campoHastaId.value),f
alse,false);
var peticion = objetoStore.openCursor(rango);
peticion.addEventListener('success',exitoCursor);
function exitoCursor(evento){
var cursor = evento.target.result;
if (cursor){
campoListaRango.innerHTML+='<li>'+cursor.primaryKey+'-'+cursor.value.titulo+'--'+cursor.value.contenido+'</li>';
cursor.continue();
//El xito de continue ser gestionado por exitoCursor
}else{
campoListaRango.innerHTML+='<li>--- FIN DE RANGO ---</li>';
}
}

14

Vous aimerez peut-être aussi