domingo, 29 de mayo de 2016

Eliminando y buscando datos con HTML, JavaScript y CSS



Eliminando datos

Hemos aprendido cómo agregar, leer y listar datos. Es hora de estudiar la posibilidad de eliminar objetos de un Almacén de Objetos. Como mencionamos anteriormente, el método delete() provisto por la API recibe un valor y elimina el objeto con la clave correspondiente a ese valor.
El código es sencillo; solo necesitamos crear un botón para cada objeto listado en pantalla y generar una transacción READ_WRITE para poder realizar la operación y eliminar el objeto:

Código 11-11. Eliminando objetos.

function mostrarlista(e){
var cursor=e.result || e.target.result;
if(cursor){
cajadatos.innerHTML+='<div>'+cursor.value.id+' -
'+cursor.value.nombre+' - '+cursor.value.fecha+' <button
onclick="eliminar(\''+cursor.value.id+'\')">Eliminar
</button></div>';
cursor.continue();
}
}
function eliminar(clave){
if(confirm('Está Seguro?')){
var transaccion=bd.transaction(['peliculas'],
IDBTransaction.READ_WRITE);
var almacen=transaccion.objectStore('peliculas');
var solicitud=almacen.delete(clave);
solicitud.addEventListener('success', mostrar, false);
}
}

El botón agregado a cada película en la función mostrarlista() del Código 11-11 contiene un manejador de eventos en línea. Cada vez que el usuario hace clic en uno de estos botones, la función eliminar() es ejecutada con el valor de la propiedad id como su atributo. Esta función genera primero una transacción READ_WRITE y luego usando la clave recibida procede a eliminar el correspondiente objeto del Almacén de Objetos peliculas.
Al final, si la operación fue exitosa, el evento success es disparado y la función mostrar() es ejecutada para actualizar la lista de películas en pantalla. 
Hágalo usted mismo: Tome el código 11-9, reemplace la función mostrarlista() y agregue la función eliminar() del código 11-11. Finalmente, abra el documento HTML del Código 11-1 para probar la aplicación. Podrá ver el listado de películas pero ahora cada línea incluye un botón para eliminar la película del Almacén de Objetos. Experimente agregando y eliminando películas.


Buscando datos

Probablemente la operación más importante realizada en un sistema de base de datos es la búsqueda. El propósito absoluto de esta clase de sistemas es indexar la información almacenada para facilitar su posterior búsqueda. Como estudiamos anteriormente es este capítulo, el método get() es útil para obtener un objeto por vez cuando conocemos el valor de su clave, pero una operación de búsqueda es usualmente más compleja que esto.
Para obtener una lista específica de objetos desde el Almacén de Objetos, tenemos que pasar un rango como argumento para el método openCursor(). La API incluye la interface IDBKeyRange con varios métodos y propiedades para declarar un rango y limitar los objetos retornados:
  • only(valor) Solo los objetos con la clave que concuerda con valor son retornados. Por ejemplo, si buscamos películas por año usando only(“1972”), solo la película El Padrino será retornada desde el almacén. 
  • bound(bajo, alto, bajoAbierto, altoAbierto) Para realmente crear un rango, debemos contar con valores que indiquen el comienzo y el final de la lista, y además especificar si esos valores también serán incluidos. El valor del atributo bajo indica el punto inicial de la lista y el atributo alto es el punto final. Los atributos bajoAbierto y altoAbierto son valores booleanos usados para declarar si los objetos que concuerdan exactamente con los valores de los atributos bajo y alto serán ignorados. Por ejemplo, bound(“1972”, “2010”, false, true) retornará una lista de películas filmadas desde el año 1972 hasta el año 2010, pero no incluirá las realizadas específicamente en el 2010 debido a que el valor es true (verdadero) para el punto donde el rango termina, lo que indica que el final es abierto y las películas de ese año no son incluidas.
  • lowerBound(valor, abierto) Este método crea un rango abierto que comenzará por valor e irá hacia arriba hasta el final de la lista. Por ejemplo, lowerBound(“1983”, true) retornará todas las películas hechas luego de 1983 (sin incluir las filmadas en ese año en particular).
  • upperBound(valor, abierto) Este es el opuesto al anterior. Creará un rango abierto, pero los objetos retornados serán todos los que posean un valor de índice menor a valor. Por ejemplo, upperBound(“1983”, false) retornará todas las películas hechas antes de 1983, incluyendo las realizadas ese mismo año (el atributo abierto fue declarado como false).

Preparemos primero una nueva plantilla para presentar un formulario en pantalla con el que se pueda buscar películas:

Código 11-12. Formulario de búsqueda.

<!DOCTYPE html>
<html lang="es">
<head>
<title>IndexedDB API</title>
<link rel="stylesheet" href="indexed.css">
<script src="indexed.js"></script>
</head>
<body>
<section id="cajaformulario">
<form name="formulario">
<p>Buscar Película por Año:<br><input type="search"
name="fecha" id="fecha"></p>
<p><input type="button" name="buscar" id="buscar"
value="Buscar"></p>
</form>
</section>
<section id="cajadatos">
No hay información disponible
</section>
</body>
</html>

Este nuevo documento HTML provee un botón y un campo de texto donde ingresar el año para buscar películas de acuerdo a un rango especificado en el siguiente código:

Código 11-13. Buscando películas.

function iniciar(){
cajadatos=document.getElementById('cajadatos');
var boton=document.getElementById('buscar');
boton.addEventListener('click', buscarobjetos, false);
if('webkitIndexedDB' in window){
window.indexedDB=window.webkitIndexedDB;
window.IDBTransaction=window.webkitIDBTransaction;
window.IDBKeyRange=window.webkitIDBKeyRange;
window.IDBCursor=window.webkitIDBCursor;
}else if('mozIndexedDB' in window){
window.indexedDB=window.mozIndexedDB;
}
var solicitud=indexedDB.open('mibase');
solicitud.addEventListener('error', errores, false);
solicitud.addEventListener('success', crear, false);
}
function errores(e){
alert('Error: '+e.code+' '+e.message);
}
function crear(e){
bd=e.result || e.target.result;
if(bd.version==''){
var solicitud=bd.setVersion('1.0');
solicitud.addEventListener('error', errores, false);
solicitud.addEventListener('success', crearbd, false);
}
}
function crearbd(){
var almacen=bd.createObjectStore('peliculas', {keyPath: 'id'});
almacen.createIndex('BuscarFecha', 'fecha', { unique: false });
}
function buscarobjetos(){
cajadatos.innerHTML='';
var buscar=document.getElementById('fecha').value;
var transaccion=bd.transaction(['peliculas']);
var almacen=transaccion.objectStore('peliculas');
var indice=almacen.index('BuscarFecha');
var rango=IDBKeyRange.only(buscar);
var cursor=indice.openCursor(rango);
cursor.addEventListener('success', mostrarlista, false);
}
function mostrarlista(e){
var cursor=e.result || e.target.result;
if(cursor){
cajadatos.innerHTML+='<div>'+cursor.value.id+' -
'+cursor.value.nombre+' - '+cursor.value.fecha+'</div>';
cursor.continue();
}
}
window.addEventListener('load', iniciar, false);

La función buscarobjetos() es la más importante del Código 11-13. En esta función generamos una transacción de solo lectura READ_ONLY para el Almacén de Objetos peliculas, seleccionamos el índice BuscarFecha para usar la propiedad fecha como índice, y creamos un rango desde el valor de la variable buscar (el año insertado en el formulario por el usuario). El método usado para construir el rango es only(), pero puede probar con cualquiera de los métodos estudiados. Este rango es pasado luego como un argumento del método openCursor(). Si la operación es exitosa, la función mostrarlista() imprimirá en pantalla la lista de películas del año seleccionado.
El método only() retorna solo las películas que concuerdan exactamente con el valor de la variable buscar. Para probar otros métodos, puede proveer valores por defecto para completar el rango, por ejemplo bound(buscar, “2011”, false, true).
El método openCursor() puede tomar dos posibles atributos al mismo tiempo. Por esta razón, una instrucción como openCursor(rango, IDBCursor.PREV) es válida y retornará los objetos en el rango especificado y en orden descendente (usando como referencia el mismo índice utilizado para el rango).
IMPORTANTE: La característica de buscar textos se encuentra bajo consideración en este momento, pero aún no ha sido desarrollada o incluso incluida en la especificación oficial. Para obtener más información sobre esta API, visite nuestro sitio web y siga los enlaces correspondientes a este capítulo.

Referencia rápida

La API IndexedDB tiene una infraestructura de bajo nivel. Los métodos y propiedades estudiados en este capítulo son solo parte de lo que esta API tiene para ofrecer. Con el propósito de simplificar los ejemplos, no seguimos ninguna estructura específica. Sin embargo, esta API, así como otras, está organizada en interfaces. Por ejemplo, existe una interface específica para tratar con la organización de la base de datos, otra para la creación y manipulación de Almacenes de Objetos, etc… Cada interface incluye sus propios métodos y propiedades, por lo que ahora vamos a presentar la información compartida en este capítulo siguiendo esta clasificación oficial.
IMPORTANTE: Las descripciones presentadas en esta referencia rápida solo muestran los aspectos más relevantes de cada interface. Para estudiar la especificación completa, visite nuestro sitio web y siga los enlaces correspondientes a este capítulo.

Interface Environment (IDBEnvironment y IDBFactory)

La interface Environment, o IDBEnvironment, incluye un atributo IDBFactory. Juntas estas interfaces proveen los elementos necesarios para operar con bases de datos: 
  • indexedDB Este atributo provee un mecanismo para acceder al sistema de base de datos indexado.
  • open(nombre) Este método abre una base de datos con el nombre especificado en su atributo. Si no existe una base de datos previa, una nueva es creada con el nombre provisto.
  • deleteDatabase(nombre) Este método elimina una base de datos con el nombre especificado en su atributo.

Interface Database (IDBDatabase)

El objeto retornado luego de la apertura o creación de una base de datos es procesado por esta interface. Con este objetivo, la interface provee varios métodos y propiedades:
  • version Esta propiedad retorna el valor de la versión actual de la base de datos abierta.
  • name Esta propiedad retorna el nombre de la base de datos abierta.
  • objectStoreNames Esta propiedad retorna un listado de los nombres de los Almacenes de Objetos contenidos dentro de la base de datos abierta.
  • setVersion(valor) Este método establece una nueva versión para la base de datos abierta. El atributo valor puede ser cualquier cadena de texto que deseemos.
  • createObjectStore(nombre, clave, autoIncremento) Este método crea un nuevo Almacén de Objetos para la base de datos abierta. El atributo nombre representa el nombre del Almacén de Objetos, clave es un índice común para todos los objetos almacenados en este almacén, y autoIncremento es un valor booleano que activa un generador automático de claves.
  • deleteObjectStore(nombre) Este método elimina un Almacén de Objetos con el nombre especificado en su atributo.
  • transaction(almacenes, tipo, máximo) Este método inicia una transacción con la base de datos. La transacción puede ser especificada para uno o más Almacenes de Objetos declarados en el atributo almacenes, y puede ser creada para diferentes tipos de acceso de acuerdo con el atributo tipo. Puede también recibir un atributo máximo en milisegundos para especificar el tiempo máximo permitido que la operación puede tardar en realizarse. Para mayor información sobre cómo configurar una transacción, ver Interface Transaction en esta Referencia rápida.

Interface Object Store (IDBObjectStore)

Esta interface incluye todos los métodos y propiedades necesarios para manipular objetos en un Almacén de Objetos (Object Store).
  • name Esta propiedad retorna el nombre del Almacén de Objetos actualmente en uso.
  • keyPath Esta propiedad retorna la clave, si existe, del Almacén de Objetos actualmente en uso (esta es la clave definida como índice común en el momento en el que el Almacén de Objetos fue creado).
  • IndexNames Esta propiedad retorna una lista de los nombres de los índices creados para el Almacén de Objetos actualmente en uso.
  • add(objeto) Este método agrega un objeto al Almacén de Objetos seleccionado con la información provista en su atributo. Si un objeto con el mismo índice ya existe, un error es retornado. El método puede recibir un par clave/valor o un objeto conteniendo varios pares clave/valor como atributo.
  • put(objeto) Este método agrega un objeto al Almacén de Objetos seleccionado con la información provista en su atributo. Si un objeto con el mismo índice ya existe, el objeto es sobrescrito con la nueva información. El método puede recibir un par clave/valor o un objeto conteniendo varios pares clave/valor como atributo.
  • get(clave) Este método retorna el objeto con el valor del índice igual a clave.
  • delete(clave) Este método elimina el objeto con el valor del índice igual a clave.
  • createIndex(nombre, propiedad, único) Este método crea un nuevo índice para el Almacén de Objetos seleccionado. El atributo nombre especifica el nombre del índice, el atributo propiedad declara la propiedad de los objetos que será asociada con este índice, y el atributo único indica si los objetos con un mismo valor de índice serán permitidos o no.
  • index(nombre) Este método activa el índice con el nombre especificado en su atributo.
  • deleteIndex(nombre) Este método elimina el índice con el nombre especificado en su atributo.
  • openCursor(rango, dirección) Este método crea un cursor para el Almacén de Objetos seleccionado. El atributo rango es un objeto range (definido por la Interface Range) para determinar cuáles objetos son seleccionados. El atributo dirección establece el orden de estos objetos. Para mayor información sobre cómo configurar y manipular un cursor, ver la Interface Cursors en esta Referencia Rápida. Para mayor información sobre cómo construir un rango con el objeto range, ver la Interface Range en esta Referencia Rápida.


Interface Cursors (IDBCursor)

Esta interface provee valores de configuración para especificar el orden de los objetos seleccionados desde el Almacén de Objetos. Estos valores deben ser declarados como el segundo atributo del método openCursor(), como en openCursor(null, IDBCursor.PREV).
  • NEXT (siguiente). Esta constante determina un orden ascendente para los objetos apuntados por el cursor (este es el valor por defecto).
  • NEXT_NO_DUPLICATE (siguiente no duplicado). Esta constante determina un orden ascendente para los objetos apuntados por el cursor e ignora los duplicados.
  • PREV (anterior). Esta constante determina un orden descendente para los objetos apuntados por el cursor.
  • PREV_NO_DUPLICATE (anterior no duplicado). Esta constante determina un orden descendente para los objetos apuntados por el cursor e ignora los duplicados.

Esta interface también provee varios métodos y propiedades para manipular los objetos apuntados por el cursor.
  • continue(clave) Este método mueve el puntero del cursor hacia el siguiente objeto en la lista o hacia el objeto referenciado por el atributo clave, si es declarado.
  • delete() Este método elimina el objeto que actualmente se encuentra apuntado por el cursor.
  • update(valor) Este método modifica el objeto actualmente apuntado por el cursor con el valor provisto por su atributo.
  • key Esta propiedad retorna el valor del índice del objeto actualmente apuntado por el cursor.
  • value Esta propiedad retorna el valor de cualquier propiedad del objeto actualmente apuntado por el cursor.
  • direction Esta propiedad retorna el orden de los objetos obtenidos por el cursor (ascendente o descendente).


Interface Transactions (IDBTransaction)

Esta interface provee valores de configuración para especificar el tipo de transacción que se va a llevar a cabo. Estos valores deben ser declarados como el segundo atributo del método transaction(), como en transaction(almacenes, IDBTransaction. READ_WRITE).
  • READ_ONLY Esta constante configura la transacción como una transacción de solo lectura (este es el valor por defecto).
  • READ_WRITE Esta constante configura la transacción como una transacción de lecturaescritura.
  • VERSION_CHANGE Este tipo de transacción es usado solamente para actualizar el número de versión de la base de datos.


Interface Range (IDBKeyRangeConstructors)

Esta interface provee varios métodos para la construcción de un rango a ser usado con cursores:
  • only(valor) Este método retorna un rango con los puntos de inicio y final iguales a valor.
  • bound(bajo, alto, bajoAbierto, altoAbierto) Este método retorna un rango con el punto de inicio declarado por bajo, el punto final declarado por alto, y si estos valores serán excluidos de la lista de objetos o no declarado por los últimos dos atributos.
  • lowerBound(valor, abierto) Este método retorna un rango comenzando por valor y terminando al final de la lista de objetos. El atributo abierto determina si los objetos que concuerdan con valor serán excluidos o no.
  • upperBound(valor, abierto) Este método retorna un rango comenzando desde el inicio de la lista de objetos y terminando en valor. El atributo abierto determina si los objetos que concuerdan con valor serán excluidos o no. 


Interface Error (IDBDatabaseException)

Los errores retornados por las operaciones en la base de datos son informados a través de esta interface.

code Esta propiedad representa el número de error.
message Esta propiedad retorna un mensaje describiendo el error.

El valor retornado también puede ser comparado con las constantes de la siguiente lista para encontrar el error correspondiente.

UNKNOWN_ERR - valor 0.
NON_TRANSIENT_ERR - valor 1.
NOT_FOUND_ERR - valor 2.
CONSTRAINT_ERR - valor 3.
DATA_ERR - valor 4.
NOT_ALLOWED_ERR - valor 5.
TRANSACTION_INACTIVE_ERR - valor 6.
ABORT_ERR - valor 7.
READ_ONLY_ERR - valor 11.
RECOVERABLE_ERR - valor 21.
TRANSIENT_ERR - valor 31.
TIMEOUT_ERR - valor 32.
DEADLOCK_ERR - valor 33.



Espero haber ayudado en algo. Hasta la próxima oportunidad!











  

No hay comentarios:

Publicar un comentario en la entrada