Saltar al contenido.
Blog de Enrique Stolar

Blog


Publicado el 19/07/2026 | Autor: Enrique Stolar

CRUD con datos en memoria

CRUD con datos en memoria

Serie: Desarrollo de Interfaces

Curso: Desarrollo de Interfaces 1

Capítulo 16: CRUD con datos en memoria

Capítulo anterior: JavaScript: arreglos y objetos

Capítulo siguiente: CRUD con archivos JSON usando Node.js

Un CRUD es una de las primeras señales de que una interfaz dejó de ser una maqueta y empezó a comportarse como una aplicación. CRUD significa crear, leer, actualizar y eliminar registros. En otras palabras: agregar un dato, mostrarlo, modificarlo y borrarlo cuando ya no corresponde.

En este capítulo todavía no usaremos base de datos, archivos ni servidor. Trabajaremos con datos en memoria: un arreglo de objetos que vive mientras la página está abierta. Cuando recargamos el navegador, los cambios se pierden. Eso no es un error; es justamente el laboratorio perfecto para entender la lógica antes de conectar JSON, Node.js o MySQL.

Qué aprenderás hoy

  • Entender qué significa CRUD desde la lógica de una interfaz.
  • Crear registros a partir de un formulario.
  • Listar registros renderizando tarjetas o filas.
  • Editar un registro existente sin duplicarlo.
  • Eliminar registros con control.
  • Separar datos, funciones y renderizado para que el código sea mantenible.
Idea clave

Un CRUD en memoria no guarda datos permanentemente, pero enseña la estructura mental que luego se usa con archivos JSON, APIs y bases de datos.

Por qué importa

Muchas personas intentan aprender bases de datos demasiado pronto. Copian consultas, conectan librerías y se frustran porque todavía no dominan lo esencial: cómo representar un registro, cómo ubicarlo en una lista, cómo modificarlo y cómo volver a pintar la interfaz después de cada acción.

Si entiendes un CRUD en memoria, luego cambiar el origen de datos será más sencillo. Hoy los datos estarán en un arreglo. Mañana vendrán de un archivo JSON. Más adelante llegarán desde MySQL. La interfaz debería mantener una lógica parecida: leer datos, procesarlos y mostrarlos de forma clara.

La estructura inicial

Empecemos con una lista de estudiantes. Cada estudiante será un objeto con un identificador único. Ese id es importante porque no siempre conviene buscar por nombre: dos personas pueden llamarse igual, pero cada registro debe tener una forma estable de ser identificado.

let students = [
  {
    id: 1,
    name: "Valeria",
    course: "Desarrollo de Interfaces",
    status: "Activo"
  },
  {
    id: 2,
    name: "Mateo",
    course: "JavaScript",
    status: "En revisión"
  }
];

let nextId = 3;
let editingId = null;

Usamos let porque el arreglo cambiará durante la interacción. nextId nos ayuda a crear identificadores nuevos. editingId nos permitirá saber si el formulario está creando un registro nuevo o actualizando uno existente.

El HTML mínimo

Necesitamos un formulario, un mensaje de estado y un contenedor donde mostraremos los registros. El diseño puede mejorar después; primero necesitamos que la lógica funcione con claridad.

<form id="student-form">
  <label for="name">Nombre</label>
  <input id="name" name="name" type="text" required>

  <label for="course">Curso</label>
  <input id="course" name="course" type="text" required>

  <label for="status">Estado</label>
  <select id="status" name="status" required>
    <option value="Activo">Activo</option>
    <option value="En revisión">En revisión</option>
    <option value="Completado">Completado</option>
  </select>

  <button type="submit">Guardar</button>
  <button type="button" id="cancel-edit">Cancelar edición</button>
</form>

<p id="app-message"></p>
<section id="student-list"></section>

Leer referencias del DOM

Antes de programar acciones, guardemos las referencias a los elementos principales. Esto evita repetir selectores y hace que el código sea más fácil de mantener.

const form = document.querySelector("#student-form");
const list = document.querySelector("#student-list");
const message = document.querySelector("#app-message");
const cancelButton = document.querySelector("#cancel-edit");

Leer: mostrar los registros

La operación de lectura consiste en mostrar el estado actual de los datos. Cada vez que agregamos, editamos o eliminamos un registro, debemos volver a renderizar la lista.

function renderStudents() {
  if (students.length === 0) {
    list.innerHTML = "<p>Todavía no hay estudiantes registrados.</p>";
    return;
  }

  list.innerHTML = students.map(function (student) {
    return `
      <article class="student-card">
        <h3>${student.name}</h3>
        <p>Curso: ${student.course}</p>
        <p>Estado: ${student.status}</p>
        <button type="button" data-action="edit" data-id="${student.id}">Editar</button>
        <button type="button" data-action="delete" data-id="${student.id}">Eliminar</button>
      </article>
    `;
  }).join("");
}

Observa los atributos data-action y data-id. Nos permitirán saber qué botón fue presionado y a qué registro pertenece. Esta técnica será útil cuando tengamos muchos elementos creados dinámicamente.

Crear: agregar un nuevo registro

Para crear un registro, leemos el formulario, construimos un objeto y lo agregamos al arreglo con push(). Luego limpiamos el formulario y renderizamos de nuevo.

function getStudentFromForm() {
  const formData = new FormData(form);

  return {
    name: String(formData.get("name")).trim(),
    course: String(formData.get("course")).trim(),
    status: String(formData.get("status"))
  };
}

function createStudent(studentData) {
  const newStudent = {
    id: nextId,
    name: studentData.name,
    course: studentData.course,
    status: studentData.status
  };

  students.push(newStudent);
  nextId++;
}

Podríamos escribir todo dentro del evento submit, pero separar funciones nos ayuda a leer mejor. Una función obtiene datos. Otra crea el registro. Otra renderiza.

Actualizar: editar sin duplicar

Editar no significa crear otro registro parecido. Editar significa encontrar el registro existente y reemplazar sus valores. Para eso usaremos findIndex(), que devuelve la posición del elemento dentro del arreglo.

function updateStudent(id, studentData) {
  const index = students.findIndex(function (student) {
    return student.id === id;
  });

  if (index === -1) {
    message.textContent = "No se encontró el registro para actualizar.";
    return;
  }

  students[index] = {
    id: id,
    name: studentData.name,
    course: studentData.course,
    status: studentData.status
  };
}

Si findIndex() devuelve -1, significa que no encontró nada. Esa pequeña validación evita errores silenciosos.

Cargar datos en el formulario

Cuando alguien presiona “Editar”, necesitamos llevar los datos del registro al formulario. Así la persona puede modificar lo necesario y guardar.

function startEdit(id) {
  const student = students.find(function (item) {
    return item.id === id;
  });

  if (!student) {
    message.textContent = "No se encontró el registro.";
    return;
  }

  form.elements.name.value = student.name;
  form.elements.course.value = student.course;
  form.elements.status.value = student.status;
  editingId = id;
  message.textContent = "Editando a " + student.name + ".";
}

Este patrón es muy común: buscar el registro, llenar campos, guardar el identificador que está en edición y dejar que el formulario haga el resto.

Eliminar: quitar un registro

Para eliminar podemos usar filter() o splice(). Aquí usaremos filter() porque produce un nuevo arreglo sin el elemento eliminado y resulta fácil de leer.

function deleteStudent(id) {
  students = students.filter(function (student) {
    return student.id !== id;
  });

  if (editingId === id) {
    resetForm();
  }
}
Cuidado

Eliminar en memoria no pide confirmación por defecto. En proyectos reales, conviene confirmar acciones destructivas y mostrar mensajes claros después de borrar.

El flujo completo del formulario

Ahora conectamos el formulario. Si editingId está vacío, creamos. Si tiene un valor, actualizamos el registro correspondiente.

function resetForm() {
  form.reset();
  editingId = null;
}

form.addEventListener("submit", function (event) {
  event.preventDefault();

  const studentData = getStudentFromForm();

  if (studentData.name === "" || studentData.course === "") {
    message.textContent = "Completa el nombre y el curso.";
    return;
  }

  if (editingId === null) {
    createStudent(studentData);
    message.textContent = "Registro creado correctamente.";
  } else {
    updateStudent(editingId, studentData);
    message.textContent = "Registro actualizado correctamente.";
  }

  resetForm();
  renderStudents();
});

Delegar eventos en la lista

Los botones de editar y eliminar se crean dinámicamente. En vez de agregar un evento a cada botón, escuchamos el clic en el contenedor y revisamos qué botón fue presionado.

list.addEventListener("click", function (event) {
  const button = event.target.closest("button");

  if (!button) {
    return;
  }

  const id = Number(button.dataset.id);
  const action = button.dataset.action;

  if (action === "edit") {
    startEdit(id);
  }

  if (action === "delete") {
    deleteStudent(id);
    message.textContent = "Registro eliminado.";
    renderStudents();
  }
});

cancelButton.addEventListener("click", function () {
  resetForm();
  message.textContent = "Edición cancelada.";
});

renderStudents();

Esto se llama delegación de eventos. Es una técnica muy útil cuando la interfaz crea elementos después de que la página ya cargó.

Variante profesional: una sola fuente de verdad

En este ejemplo, la fuente de verdad es el arreglo students. La interfaz se renderiza a partir de ese arreglo. Si el arreglo cambia, volvemos a pintar. Esa idea será esencial cuando usemos React más adelante: el estado cambia y la vista se actualiza.

La regla práctica es sencilla: no intentes que el HTML sea tu base de datos. Los datos deben estar en una estructura controlada. El HTML debe ser la representación visual de esos datos.

Errores comunes

  • Crear un registro nuevo cuando en realidad se quería editar uno existente.
  • Olvidar guardar el id del registro que está en edición.
  • Usar nombres como identificadores únicos.
  • No volver a renderizar la lista después de crear, editar o eliminar.
  • Mezclar toda la lógica dentro de un solo evento enorme.
  • Asumir que los datos en memoria se guardan al recargar la página.

Buenas prácticas

  • Usa identificadores únicos para cada registro.
  • Separa funciones para crear, leer, actualizar y eliminar.
  • Mantén una sola fuente de verdad para los datos.
  • Vuelve a renderizar la interfaz después de cada cambio.
  • Valida los datos antes de guardarlos en el arreglo.
  • Confirma acciones destructivas cuando el proyecto lo requiera.
Recomendación profesional

Cuando tu CRUD crezca, organiza el código por responsabilidades: datos iniciales, selectores del DOM, funciones CRUD, funciones de renderizado y eventos. Ese orden facilita encontrar errores.

Reto práctico

Reto

Crea un CRUD en memoria para proyectos de clase. Cada proyecto debe tener id, nombre, equipo, curso y estado. Permite crear, listar, editar y eliminar proyectos desde la interfaz.

Variante extra: agrega un filtro para mostrar solo proyectos “En proceso”, “Publicado” o “Pendiente”. Si quieres subir el nivel, muestra un contador con el total de registros visibles.

Conclusión

Un CRUD en memoria parece pequeño, pero contiene la arquitectura básica de muchas aplicaciones: datos, acciones, renderizado y eventos. La diferencia con una aplicación real no está en la idea, sino en dónde viven los datos. Hoy viven en un arreglo. Después vivirán en un archivo JSON, en una API o en una base de datos.

Dominar este paso te da una ventaja enorme. Ya no ves un formulario como una pieza aislada, sino como una entrada para modificar el estado de una aplicación. Ya no ves una lista como HTML estático, sino como una representación de datos que puede cambiar.

En el siguiente capítulo llevaremos esta misma lógica a un archivo JSON usando Node.js. Ahí empezaremos a persistir datos y a entender por qué el backend no reemplaza la lógica de interfaz, sino que la complementa.

Fuentes consultadas

Compartir este artículo: