Introducción a pouchdb: app de agenda

Introducción

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.

Ejemplo práctico: app de agenda

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:

Preparando scripts

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.

Instanciar base de datos

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.

Insertar, create o agregar

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í:

Listar, o mostrar

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.

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.

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;
}

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

1 comentario en “Introducción a pouchdb: app de agenda”

  1. Pingback: Almacenamiento en el navegador con JavaScript y localStorage - Parzibyte's blog

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *