Este artículo es el tercero de una serie de 3 artículos:
- Almacenamiento con HTML5: Almacenamiento local
- Almacenamiento con HTML5: Almacenamiento SQL
- Almacenamiento con HTML5: Ejemplo de aplicación
Aquí no voy a hablar de nada nuevo, solo voy a exponer una sencilla aplicación de listado de tareas que aprovecha los dos tipos de almacenamiento previamente explicados, así que si no los has leido ahora es buena idea hacerlo antes de continuar.
La aplicación
La aplicación consiste en una sencilla lista de tareas. Estas tareas se almacenan en la base de datos local utilizando Almacenamiento SQL.
Por defecto, la primera vez que entres en la página de demostración se crearán las tablas necesarias en tu navegador y creará una variable local que determinará el orden en que se muestran las tareas. Esto se realiza mediante Almacenamiento local.
[Nota][nota]
Creando tareas
Para crear una tarea solo es necesario introducir un titulo. Al aceptar se insertará en la base de datos con el valor del campo "done" a 0. Esto la marcará como no realizada y se refrescará la lista de tareas.
En este momento puedes recargar la página (o incluso cerrar el navegador y volver a la aplicación) y la información seguirá estando ahí.
Actualizando tareas
Cada tarea ofrece un checkbox que indica el si la tarea está realizada o no. Al cambiar el estado de este checkbox se actualizará el registro en la base de datos.
Reordenando las tareas
En el panel lateral se encuentran las opciones de ordenación, por nombre o por fecha de creación y en orden ascendente o descendente. Al pinchar en uno de estos enlaces se actualizará la variable local en nuestro navegador y se refrescará la lista de tareas para reflejar el nuevo order.
Si creas otra tarea se insertará en el orden seleccionado.
El código
A continuación algunas partes del código comentadas.
HTML
El HTML principal es este:
<button id="newListButton">Nueva lista</button>
<form id="newList" style="display:none">
<label for="newListName">Titulo</label><br />
<input id="newListName" type="text" /><br />
<input type="submit" value="Crear" /> o <a id="newListCancel" href="#">Cancelar</a>
</form>
<p></p>
<h3>Listado de tareas</h3>
<ul id="list"></ul>
Un formulario, oculto por defecto, que permitirá la creación de tareas y una lista donde se listarán. En el HTML está vacía, y que cada vez que recargemos la página se consultará la base de datos y se crearan los elementos de la lista necesarios. Estos elementos tienen la siguiente forma:
<li id="item-1">
<span class="item-done"><input type="checkbox"></span>
<span class="item-name">Tarea 1</span>
<span class="item-created">11/05/2010 12:27</span>
</li>
Para las opciones de ordenación:
<h3>Opciones</h3>
Ordenar por nombre <a class="order-link" data-field="name" data-order="ASC" href="#">ASC</a> |
<a class="order-link" data-field="name" data-order="DESC" href="#">DESC</a><br />
Ordenar por fecha de creación <a class="order-link" data-field="timestamp" data-order="ASC" href="#">ASC</a> |
<a class="order-link" data-field="timestamp" data-order="DESC" href="#">DESC</a>
Los enlaces de ordenación tienen la clase .order-link y dos atributos data-, uno para el campo de la ordenación (name|timestamp) y otro para el tipo de orden (ASC|DESC).
Javascript
La aplicación utiliza jQuery para manipular el DOM y el manejo de eventos. El código Javascript se divide en 3 partes:
Objeto List
Se encaga de controlar la lista de tareas a nivel de DOM.
var List = {
// Refresca la lista de tareas
refresh: function(){
$('#list').empty();
App.all();
},
// Inserta una nueva tarea en la lista
create: function(item){
var li = $('<li>', {id: 'item-'+ item.id}).data('id', item.id);
var name = $('<span>', {className: 'item-name'}).text(item.name);
var done = $('<span>', {className: 'item-done'}).append(
$('<input>', {type: 'checkbox', checked: (item.done == 1)})
);
var d = new Date(item.timestamp);
var d_string = d.getDate()+"/"+ (d.getMonth() < 10 ? "0"+d.getMonth() : d.getMonth())+"/"+d.getFullYear() + " " + d.getHours()+":"+d.getMinutes();
var created = $('<span>', {className: 'item-created'}).text(d_string);
li.append(done).append(name).append(created).appendTo('#list');
},
// Muestra/Oculta el formulario de nueva tarea
toggleNew: function(){
$('#newList').toggle();
$('#newListButton').toggle();
if($('#newList').is(':visible')){
$('#newListName').focus();
}else{
$('#newListName').attr('value', '');
}
}
};
Objecto App
Se encarga de la gestión de la base de datos.
var App = {
_db: null,
// Crea la tabla de tareas
createTables: function(){
if(!this._db) this._prepareDatabase();
this._db.transaction(
function(tx){
var sql = "CREATE TABLE IF NOT EXISTS items(id INTEGER PRIMARY KEY, name TEXT, done INTEGER, timestamp REAL)";
tx.executeSql(sql);
}
);
},
// Obtiene todas las tareas ordenadas, para ello utiliza la variable local 'order'
all: function(){
if (!this._db) this._prepareDatabase();
this._db.transaction(
function(tx){
var sql = "SELECT * FROM items order by " + localStorage.getItem('order');
tx.executeSql(sql,[],
function(tx, result){
if(result.rows.length > 0){
for(var i=0; i < result.rows.length; i++){
var item = result.rows.item(i);
List.create({id:item.id, name: item.name, done: item.done, timestamp: item.timestamp});
}
}
}
);
}
)
return [];
},
// Inserta una nueva tarea en la base de datos
insert: function(name){
if(!this._db) this._prepareDatabase();
var sql = "INSERT INTO items (name, done, timestamp) values(?,?,?)";
var id;
this._db.transaction(
function(tx){
tx.executeSql(sql, [name, 1, new Date().getTime()],
function(tx,result){
List.refresh();
},
function(tx, error){
alert("Error de base de datos: " + error.message);
}
)
}
);
},
// Marca una tarea como realizada
done: function(id){
if(!this._db) this._prepareDatabase();
var sql = "UPDATE items SET done=? WHERE id=?";
this._db.transaction(
function(tx){
tx.executeSql(sql, [1, id],
function(tx,result){
console.info(result);
List.refresh();
}
)
}
);
},
// Marca una tarea como no realizada
undone: function(id){
if(!this._db) this._prepareDatabase();
var sql = "UPDATE items SET done=? WHERE id=?";
this._db.transaction(
function(tx){
tx.executeSql(sql, [0, id],
function(tx,result){
List.refresh();
}
)
}
);
},
// Borra la tabla de tareas
dropTable: function(){
if(!this._db) this._prepareDatabase();
this._db.transaction(
function(tx){
var sql = "DROP TABLE items";
tx.executeSql(sql, [],
function(tx, result){},
function(tx, error){
alert('Error: ' + error.message);
}
);
}
);
},
// Conecta con la base de datos
_prepareDatabase: function(){
this._db = openDatabase("TodoList", "1.0", "Sencillo todo list", 5*1024*1024);
if (!this._db) alert('No se puede establecer conexión con la base de datos.');
}
};
Eventos e inicializaciones
Añade funciones a los eventos para el funcionamiento de la aplicación. Añade el comportamiento de los botones de formulario y los enlaces de ordenación. Establece el orden si ya existe o crea uno por defecto, crea las tablas si es necesario y carga las tareas anteriormente creadas.
$(document).ready(function(){
// muestra el formulario y oculta el boton de "nuevo"
$('#newListButton').click(function(){
List.toggleNew();
});
// cancela la edición
$('#newListCancel').click(function(){
List.toggleNew();
});
// Crea la lista de tareas
$('#newList').submit(function(e){
e.preventDefault();
var name = $('#newListName').attr('value');
if (name.length > 2){
App.insert(name);
}else{
alert("El nombre de la lista de tareas no puede estar en blanco y debe tener 3 carácteres o más");
}
List.toggleNew();
});
// Actualización de tareas
$('#list input[type=checkbox]').live('click',function(){
if($(this).is(':checked')){
App.done($(this).parents('li').data('id'));
}else{
App.undone($(this).parents('li').data('id'));
}
});
// Orden de las tareas
$('.order-link').click(function(e){
e.preventDefault();
var order = $(this).attr('data-field') + " " +$(this).attr('data-order');
localStorage.setItem('order', order);
List.refresh();
});
// Orden de las tareas
try{
localStorage.getItem('order');
}catch(e){
localStorage.setItem('order', 'timestamp DESC');
}
// Creación de las tablas si es necesario
App.createTables();
// Carga las tareas en el DOM
List.refresh();
});
Nota
Aquí no he intentado construir la mejor aplicación para administrar tareas, sino de hacer una aplicación muy básica y simple a efectos de demostración.
Soy consciente de que el código aquí mostrado podría reorganizarse mejor, además de refactorizarlo para eliminar código repetido.
Podría añadir funcionalidades interesantes como reordenación (drag&drop), edición y eliminación de tareas, etc, pero lo importante es coger la idea.
Os animo a mejorar este ejemplo.
