PouchDB es una base de datos que se sincroniza. Permite trabajar offline y online, con una sincronización de la cual no tenemos que encargarnos nosotros. Esto viene perfecto para desarrollar sistemas que trabajan tanto con y sin internet.
Es importante notar que si no queremos la sincronización, no pasa nada. Por lo que no estamos obligados a sincronizar ningún sólo dato.
Además, la base de datos se sincroniza entre todos los dispositivos. Esto es algo muy genial, aunque claro, tampoco es tan poderosa como MySQL, MS SQL Server, etcétera.
Finalmente cabe mencionar que todo se guarda en el navegador del usuario, evitando así tener que programar el lado del servidor.
En este tutorial vamos a estar trabajando para crear una agenda, de esas en donde guardas el teléfono, dirección y nombre de tus conocidos.
Si quieres descargar el código completo puedes ir a GitHub: https://github.com/parzibyte/agenda_pouchdb
También existe una demo en esa misma página: https://parzibyte.github.io/agenda_pouchdb/
Finalmente aquí dejo un GIF de cómo se ve al usarla:
Simplemente tenemos que incluir la librería de PouchDB en nuestro archivo html y listo. En este caso no utilizaremos ningún framework. Así que para descargar la última versión (independientemente de cuándo se consulte este tutorial) podemos ir a https://pouchdb.com/download.html y copiar el quick start.
En mi caso, incluiré el archivo http://cdn.jsdelivr.net/npm/pouchdb@6.4.1/dist/pouchdb.min.js
Una vez incluido el archivo, vamos a ver si PouchDB está definido para comenzar a trabajar. Así que ejecutaremos este código:
<!DOCTYPE html>
<html>
<head>
<script src="http://cdn.jsdelivr.net/npm/pouchdb@6.4.1/dist/pouchdb.min.js"></script>
</head>
<body>
<h1 id="resultado"></h1>
<script>
var elemento = document.querySelector("#resultado");
if ("undefined" !== typeof PouchDB) elemento.innerHTML = "Correcto";
else elemento.innerHTML = "Algo salió mal";
</script>
</body>
</html>
Si al abrirlo nos dice “Correcto” entonces todo está bien. Si no, por favor revisa que hayas incluido bien el script y que tengas una conexión a internet, o el archivo descargado.
Vamos a ver todas las operaciones como lo son crear, leer, actualizar y eliminar datos. Además de ver cómo los índices y los prefijos nos van a ayudar.
Lo que no veremos será la sincronización y los selectores (si vienes de una base de datos relacional, los selectores son el equivalente a la cláusula WHERE) ya que se alargaría mucho este post.
En PouchDB sólo tenemos una base de datos que alberga documentos. No existen las tablas. Así que todo se guarda en un mismo lugar.
Una vez dicho esto, para usar PouchDB simplemente tenemos que llamar al constructor con el nombre de la base de datos deseada. Si no existe, se creará y se devolverá la instancia en donde ya podremos guardar.
La primera operación que tenemos que hacer para poder hacer todas las demás, porque si no existen datos, ¿qué cosas se eliminan o actualizan?
Vamos a comenzar creando el formulario por donde entrarán los datos. No pondré estilos, pues creo que hacen que el lector se confunda. Así que vamos a enfocarnos únicamente en el funcionamiento de la app.
<!DOCTYPE html>
<html>
<head>
<script src="http://cdn.jsdelivr.net/npm/pouchdb@6.4.1/dist/pouchdb.min.js"></script>
</head>
<body>
<h1>Agenda con PouchDB</h1>
<a target="_blank" href="https://www.parzibyte.me/blog">https://www.parzibyte.me/blog</a>
<br />
<label for="nombreCompleto">Nombre:</label>
<br>
<input placeholder="Nombre y apellidos" type="text" name="nombreCompleto" id="nombreCompleto" />
<br>
<br>
<label for="direccion">Dirección:</label>
<br>
<textarea placeholder="Escribe la dirección..." name="direccion" id=direccion rows="4" cols="30"></textarea>
<br>
<br>
<label for="telefono">Teléfono:</label>
<br>
<input placeholder="Por ejemplo, 555 222 333" type="tel" name="telefono" id="telefono" />
<br>
<br>
<button id="btnGuardar">Guardar</button>
<script src="script.js"></script>
</body>
</html>
var bd = new PouchDB("agenda"),
$nombreCompleto = document.querySelector("#nombreCompleto"),
$direccion = document.querySelector("#direccion"),
$telefono = document.querySelector("#telefono"),
$btnGuardar = document.querySelector("#btnGuardar");
$btnGuardar.addEventListener("click", function() {
var nombreCompleto = $nombreCompleto.value,
direccion = $direccion.value,
telefono = $telefono.value;
if (nombreCompleto && direccion && telefono) {
bd.post({
nombre: nombreCompleto,
direccion: direccion,
telefono: telefono
})
.then(function(respuesta) {
if (respuesta.ok) {
alert("Guardado correctamente");
}
});
}
});
Podemos ejecutar lo que llevamos hasta el momento, y se debe ver así:
Ya insertamos datos, pero para editar o eliminar hay que primero poder verlos para interactuar con ellos. Así como fue fácil insertar, será fácil recuperar los documentos.
Dejaré el código y lo explicamos abajo.
<!DOCTYPE html>
<html>
<head>
<script src="http://cdn.jsdelivr.net/npm/pouchdb@6.4.1/dist/pouchdb.min.js"></script>
<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body>
<h1>Agenda con PouchDB</h1>
<a target="_blank" href="https://www.parzibyte.me/blog">https://www.parzibyte.me/blog</a>
<br />
<label for="nombreCompleto">Nombre:</label>
<br>
<input placeholder="Nombre y apellidos" type="text" name="nombreCompleto" id="nombreCompleto" />
<br>
<br>
<label for="direccion">Dirección:</label>
<br>
<textarea placeholder="Escribe la dirección..." name="direccion" id=direccion rows="4" cols="30"></textarea>
<br>
<br>
<label for="telefono">Teléfono:</label>
<br>
<input placeholder="Por ejemplo, 555 222 333" type="tel" name="telefono" id="telefono" />
<br>
<br>
<button id="btnGuardar">Guardar</button>
<br /><br />
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Dirección</th>
<th>Teléfono</th>
</tr>
</thead>
<tbody id="cuerpoTabla">
</tbody>
</table>
<script src="script.js"></script>
</body>
</html>
var bd = new PouchDB("agenda"),
$nombreCompleto = document.querySelector("#nombreCompleto"),
$direccion = document.querySelector("#direccion"),
$telefono = document.querySelector("#telefono"),
$btnGuardar = document.querySelector("#btnGuardar"),
$cuerpoTabla = document.querySelector("#cuerpoTabla"); //Ojo: nueva variable aquí
$btnGuardar.addEventListener("click", function() {
var nombreCompleto = $nombreCompleto.value,
direccion = $direccion.value,
telefono = $telefono.value;
if (nombreCompleto && direccion && telefono) {
bd.post({
nombre: nombreCompleto,
direccion: direccion,
telefono: telefono
})
.then(function(respuesta) {
if (respuesta.ok) {
consultarContactos();
alert("Guardado correctamente");
}
});
}
});
var consultarContactos = function() {
bd.allDocs({
include_docs: true
}).then(function(documentos) {
var htmlCuerpoTabla = "";
for (var i = 0; i < documentos.rows.length; i++) {
var contacto = documentos.rows[i].doc;
htmlCuerpoTabla += "<tr>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += contacto.nombre;
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += contacto.direccion;
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += contacto.telefono;
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "</tr>";
}
$cuerpoTabla.innerHTML = htmlCuerpoTabla; //Asignar HTML concatenado
});
};
consultarContactos();
table {
border-collapse: collapse;
}
table,
th,
td {
border: 1px solid black;
}
Comencemos viendo que al script le agregamos una nueva variable, la cual es el elemento que apunta al cuerpo de la tabla.
Creamos una función que obtiene los documentos de la base de datos, que más o menos funciona como lo dice la documentación oficial.
Recorremos las filas y a una variable le vamos añadiendo código HTML para la tabla. Una vez que el ciclo termina asignamos todo ese HTML al elemento declarado previamente. Y así de fácil es.
Finalmente cabe mencionar que añadimos la hoja de estilos para los bordes de las tabla, pero podemos no incluirlos si no queremos.
Veamos ahora cómo editar.
Una vez que ya hemos creado nuestros datos es hora de poder editarlos. Dejaré la operación de eliminación para más tarde, ya que, según yo, es la más fácil.
Por ahora hay que agregar un botón a la tabla para escucharlo más tarde y consultar el contacto.
Una vez que hayamos consultado el contacto, tenemos que llenar automáticamente los campos de texto. Es decir, vamos a reutilizarlos.
Y para guardar cambios al editar crearemos otro botón que está oculto y que se muestra sólo cuando hacemos click en editar en un campo de la tabla de contactos.
<!DOCTYPE html>
<html>
<head>
<script src="http://cdn.jsdelivr.net/npm/pouchdb@6.4.1/dist/pouchdb.min.js"></script>
<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body>
<h1>Agenda con PouchDB</h1>
<a target="_blank" href="https://www.parzibyte.me/blog">https://www.parzibyte.me/blog</a>
<br />
<label for="nombreCompleto">Nombre:</label>
<br>
<input placeholder="Nombre y apellidos" type="text" name="nombreCompleto" id="nombreCompleto" />
<br>
<br>
<label for="direccion">Dirección:</label>
<br>
<textarea placeholder="Escribe la dirección..." name="direccion" id=direccion rows="4" cols="30"></textarea>
<br>
<br>
<label for="telefono">Teléfono:</label>
<br>
<input placeholder="Por ejemplo, 555 222 333" type="tel" name="telefono" id="telefono" />
<br>
<br>
<button id="btnGuardar">Guardar</button>
<button id="btnGuardarCambios">Guardar cambios</button>
<button id="btnCancelarEdicion">Cancelar</button>
<br />
<br />
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Dirección</th>
<th>Teléfono</th>
<th>Editar</th>
</tr>
</thead>
<tbody id="cuerpoTabla">
</tbody>
</table>
<script src="script.js"></script>
</body>
</html>
var bd = new PouchDB("agenda"),
$nombreCompleto = document.querySelector("#nombreCompleto"),
$direccion = document.querySelector("#direccion"),
$telefono = document.querySelector("#telefono"),
$btnGuardar = document.querySelector("#btnGuardar"),
$btnGuardarCambios = document.querySelector("#btnGuardarCambios"),
$btnCancelarEdicion = document.querySelector("#btnCancelarEdicion"),
$cuerpoTabla = document.querySelector("#cuerpoTabla"),
idTemporalContacto = null, //Variable global para referirnos al id del contacto editado
revTemporalContacto = null; //Variable global para referirnos a la revisión del contacto editado
$btnGuardar.addEventListener("click", function() {
var nombreCompleto = $nombreCompleto.value,
direccion = $direccion.value,
telefono = $telefono.value;
if (nombreCompleto && direccion && telefono) {
bd.post({
nombre: nombreCompleto,
direccion: direccion,
telefono: telefono
})
.then(function(respuesta) {
if (respuesta.ok) {
consultarContactos();
alert("Guardado correctamente");
}
});
}
});
$btnCancelarEdicion.addEventListener("click", function() {
cancelarEdicion();
});
$btnGuardarCambios.addEventListener("click", function() {
var nombreCompleto = $nombreCompleto.value,
direccion = $direccion.value,
telefono = $telefono.value;
if (nombreCompleto && direccion && telefono) {
bd.put({
nombre: nombreCompleto,
direccion: direccion,
telefono: telefono,
_id: idTemporalContacto,
_rev: revTemporalContacto
})
.then(function(respuesta) {
if (respuesta.ok) {
consultarContactos();
alert("Cambios guardados");
cancelarEdicion();
}
});
}
});
var prepararParaEditar = function() {
$btnGuardar.style.display = "none";
$btnGuardarCambios.style.display = "block";
$btnCancelarEdicion.style.display = "block";
};
var cancelarEdicion = function() {
$btnGuardar.style.display = "block";
$btnGuardarCambios.style.display = "none";
$btnCancelarEdicion.style.display = "none";
$nombreCompleto.value = $direccion.value = $telefono.value = "";
idTemporalContacto = null;
revTemporalContacto = null;
};
var consultarContactos = function() {
bd.allDocs({
include_docs: true
}).then(function(documentos) {
var htmlCuerpoTabla = "";
for (var i = 0; i < documentos.rows.length; i++) {
var contacto = documentos.rows[i].doc;
htmlCuerpoTabla += "<tr>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += contacto.nombre;
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += contacto.direccion;
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += contacto.telefono;
htmlCuerpoTabla += "</td>";
/*
Atención aquí:
Agregamos un botón con la clase btn-editar para escucharlo
más tarde
Agregamos un atributo data-id para guardar el id, ya que
al escuchar el evento click vamos a recuperar dicho atributo
*/
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += "<button class='btn-editar' data-id-contacto='" + contacto._id + "'>Editar</button>";
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "</tr>";
}
$cuerpoTabla.innerHTML = htmlCuerpoTabla; //Asignar HTML concatenado
//Una vez dibujados los botones, los escuchamos
escucharBotonesEditar();
});
};
var escucharBotonesEditar = function() {
var botonesEditar = document.getElementsByClassName("btn-editar");
for (var i = 0; i < botonesEditar.length; i++) {
botonesEditar[i].addEventListener('click', editarContacto);
}
};
var editarContacto = function() {
// Acceder a data-id-contacto
// Javascript remueve los guiones y el "data", luego
// pone todos los datos en un objeto llamado dataset
// y convierte dichos guiones a camelCase
// Ejemplo: data-id-contacto => dataset.idContacto
var idContacto = this.dataset.idContacto;
obtenerUnContacto(idContacto).then(function(contacto) {
//Ocultar y mostrar botones respectivos
prepararParaEditar();
$nombreCompleto.value = contacto.nombre;
$direccion.value = contacto.direccion;
$telefono.value = contacto.telefono;
idTemporalContacto = contacto._id;
revTemporalContacto = contacto._rev;
});
};
var obtenerUnContacto = function(idContacto) {
return bd.get(idContacto).then(function(contacto) {
return contacto;
});
};
//Por defecto, ocultar botones que sólo se muestran al editar
cancelarEdicion();
consultarContactos();
table {
border-collapse: collapse;
}
table,
th,
td {
border: 1px solid black;
}
Analicemos las nuevas cosas del código. Para editar un documento usamos el método put de PouchDB. Pero para ello necesitamos el id y la revisión de dicho documento, los cuales almacenamos en una variable temporal global.
También estamos ocultando y mostrando botones, así como leyendo datos con los atributos data-*. Sigamos para ver ahora el último paso: eliminar.
Finalmente añadiremos el botón de eliminar. El proceso es el mismo que para editar, sólo que ahora la clase de los botones será btn-eliminar en lugar de btn-editar. De ahí simplemente obtenemos el contacto usando el id recuperado de los atributos data.
Mandamos una confirmación y en caso de que se acepte, se elimina completamente. Después refrescamos la tabla y limpiamos el formulario.
El código final queda así:
<!DOCTYPE html>
<html>
<head>
<script src="http://cdn.jsdelivr.net/npm/pouchdb@6.4.1/dist/pouchdb.min.js"></script>
<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body>
<h1>Agenda con PouchDB</h1>
<a target="_blank" href="https://www.parzibyte.me/blog">https://www.parzibyte.me/blog</a>
<br />
<label for="nombreCompleto">Nombre:</label>
<br>
<input placeholder="Nombre y apellidos" type="text" name="nombreCompleto" id="nombreCompleto" />
<br>
<br>
<label for="direccion">Dirección:</label>
<br>
<textarea placeholder="Escribe la dirección..." name="direccion" id=direccion rows="4" cols="30"></textarea>
<br>
<br>
<label for="telefono">Teléfono:</label>
<br>
<input placeholder="Por ejemplo, 555 222 333" type="tel" name="telefono" id="telefono" />
<br>
<br>
<button id="btnGuardar">Guardar</button>
<button id="btnGuardarCambios">Guardar cambios</button>
<button id="btnCancelarEdicion">Cancelar</button>
<br />
<br />
<table>
<thead>
<tr>
<th>Nombre</th>
<th>Dirección</th>
<th>Teléfono</th>
<th>Editar</th>
<th>Eliminar</th>
</tr>
</thead>
<tbody id="cuerpoTabla">
</tbody>
</table>
<script src="script.js"></script>
</body>
</html>
var bd = new PouchDB("agenda"),
$nombreCompleto = document.querySelector("#nombreCompleto"),
$direccion = document.querySelector("#direccion"),
$telefono = document.querySelector("#telefono"),
$btnGuardar = document.querySelector("#btnGuardar"),
$btnGuardarCambios = document.querySelector("#btnGuardarCambios"),
$btnCancelarEdicion = document.querySelector("#btnCancelarEdicion"),
$cuerpoTabla = document.querySelector("#cuerpoTabla"),
idTemporalContacto = null, //Variable global para referirnos al id del contacto editado
revTemporalContacto = null; //Variable global para referirnos a la revisión del contacto editado
$btnGuardar.addEventListener("click", function() {
var nombreCompleto = $nombreCompleto.value,
direccion = $direccion.value,
telefono = $telefono.value;
if (nombreCompleto && direccion && telefono) {
bd.post({
nombre: nombreCompleto,
direccion: direccion,
telefono: telefono
})
.then(function(respuesta) {
if (respuesta.ok) {
consultarContactos();
alert("Guardado correctamente");
cancelarEdicion(); //Pequeño truco para limpiar el formulario
}
});
}
});
$btnCancelarEdicion.addEventListener("click", function() {
cancelarEdicion();
});
$btnGuardarCambios.addEventListener("click", function() {
var nombreCompleto = $nombreCompleto.value,
direccion = $direccion.value,
telefono = $telefono.value;
if (nombreCompleto && direccion && telefono) {
bd.put({
nombre: nombreCompleto,
direccion: direccion,
telefono: telefono,
_id: idTemporalContacto,
_rev: revTemporalContacto
})
.then(function(respuesta) {
if (respuesta.ok) {
consultarContactos();
alert("Cambios guardados");
cancelarEdicion();
}
});
}
});
var prepararParaEditar = function() {
$btnGuardar.style.display = "none";
$btnGuardarCambios.style.display = "block";
$btnCancelarEdicion.style.display = "block";
};
var cancelarEdicion = function() {
$btnGuardar.style.display = "block";
$btnGuardarCambios.style.display = "none";
$btnCancelarEdicion.style.display = "none";
$nombreCompleto.value = $direccion.value = $telefono.value = "";
idTemporalContacto = null;
revTemporalContacto = null;
};
var consultarContactos = function() {
bd.allDocs({
include_docs: true
}).then(function(documentos) {
var htmlCuerpoTabla = "";
for (var i = 0; i < documentos.rows.length; i++) {
var contacto = documentos.rows[i].doc;
htmlCuerpoTabla += "<tr>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += contacto.nombre;
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += contacto.direccion;
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += contacto.telefono;
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += "<button class='btn-editar' data-id-contacto='" + contacto._id + "'>Editar</button>";
htmlCuerpoTabla += "</td>";
//Nuevo botón
htmlCuerpoTabla += "<td>";
htmlCuerpoTabla += "<button class='btn-eliminar' data-id-contacto='" + contacto._id + "'>Eliminar</button>";
htmlCuerpoTabla += "</td>";
htmlCuerpoTabla += "</tr>";
}
$cuerpoTabla.innerHTML = htmlCuerpoTabla; //Asignar HTML concatenado
//Una vez dibujados los botones, los escuchamos
escucharBotonesEditar();
escucharBotonesEliminar();
});
};
var escucharBotonesEditar = function() {
var botonesEditar = document.getElementsByClassName("btn-editar");
for (var i = 0; i < botonesEditar.length; i++) {
botonesEditar[i].addEventListener('click', editarContacto);
}
};
var escucharBotonesEliminar = function() {
var botonesEliminar = document.getElementsByClassName("btn-eliminar");
for (var i = 0; i < botonesEliminar.length; i++) {
botonesEliminar[i].addEventListener('click', eliminarContacto);
}
};
var eliminarContacto = function() {
//Detener si no se confirma
if (!confirm("¿Seguro?")) return;
var idContacto = this.dataset.idContacto;
obtenerUnContacto(idContacto)
.then(function(contacto) {
return contacto;
})
.then(function(contacto) {
return bd.remove(contacto).then(function(respuesta) {
return respuesta;
});
})
.then(function(respuesta) {
if (respuesta.ok) {
alert("Eliminado correctamente");
consultarContactos();
}
});
};
var editarContacto = function() {
// Acceder a data-id-contacto
// Javascript remueve los guiones y el "data", luego
// pone todos los datos en un objeto llamado dataset
// y convierte dichos guiones a camelCase
// Ejemplo: data-id-contacto => dataset.idContacto
var idContacto = this.dataset.idContacto;
obtenerUnContacto(idContacto).then(function(contacto) {
//Ocultar y mostrar botones respectivos
prepararParaEditar();
$nombreCompleto.value = contacto.nombre;
$direccion.value = contacto.direccion;
$telefono.value = contacto.telefono;
idTemporalContacto = contacto._id;
revTemporalContacto = contacto._rev;
});
};
var obtenerUnContacto = function(idContacto) {
return bd.get(idContacto).then(function(contacto) {
return contacto;
});
};
//Por defecto, ocultar botones que sólo se muestran al editar
cancelarEdicion();
consultarContactos();
table {
border-collapse: collapse;
}
table,
th,
td {
border: 1px solid black;
}
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.
Ver comentarios