Cómo Resolver la Carga de Datos en Cascada, Pais, Departamento, Ciudad
Si alguna vez has intentado poblar menús desplegables (selects) de manera secuencial, como en un formulario de dirección, probablemente te has encontrado con un problema común: la asincronía.
Intentas cargar los países, luego los departamentos y finalmente las ciudades. Pero, cuando llega el momento de establecer los valores por defecto (ej. para editar un perfil), el código falla porque las opciones aún no han sido cargadas en el DOM. selectElement.value = 'Colombia' no funciona si la opción 'Colombia' todavía no existe.
Este artículo te muestra una solución elegante y robusta que me funcionó como un reloj. (Observar que puede ser usada para culquier proceso que involucre algun tipo de jerarquía de varios niveles yo tengo algunas del tipo usada para categorias de productos de almacén, ventas inventarios, así como estructuras de comercializadores , agencias, sucursales y promotores y vendedores)
El Problema: El Flujo Asíncrono y los Callbacks
El problema surge porque las funciones que cargan las listas (como Paises(), Departamentos(), y Ciudades()) son asíncronas. Utilizan fetch para obtener datos de una API, y esa operación no se completa de inmediato. El código de JavaScript sigue su camino sin esperar, y cuando intentas asignar un valor, las opciones del select están vacías.
El código tradicional con callbacks anidados termina siendo difícil de leer y mantener, lo que se conoce como "callback hell".
La Solución: async/await y la Promesa de Sincronización
Mi solución fue combinar el poder de async/await con una técnica para envolver las funciones de callback en promesas. Esto nos permite simular un comportamiento síncrono y asegurar que cada paso se complete antes de pasar al siguiente.
El Código Genérico de la Solución
A continuación, el patrón de código que utilicé. Puedes adaptar las variables y los nombres de las funciones a tu propio proyecto.
// Función para cargar y establecer las ubicaciones en cascada
const cargarUbicacionesEdicion = async (userData) => {
try {
// 1. PAÍS: Cargar países y esperar a que el DOM se actualice
if (userData.clientePaisNombre) {
await new Promise(resolve => {
// Guarda la función de callback original
const originalCallback = window.PaisesMostrar;
// Sobreescribe la función para que resuelva nuestra promesa
window.PaisesMostrar = function (data) {
originalCallback(data); // Llama a la función original para poblar el select
resolve(); // Resuelve la promesa
};
// Llama a la función asíncrona original
Paises("paisNombre");
});
// Asigna el valor del país
document.getElementById("paisNombre").value = userData.clientePaisNombre;
}
// 2. DEPARTAMENTO: Cargar departamentos y esperar
if (userData.clienteDepartamentoNombre) {
await new Promise(resolve => {
const originalCallback = window.DepartamentosMostrar;
window.DepartamentosMostrar = function (data) {
originalCallback(data);
resolve();
};
// Llama a la función de departamentos. Asegúrate de pasar el ID del país si es necesario.
Departamentos("departamentoNombre", userData.clientePaisNombre);
});
// Asigna el valor del departamento
document.getElementById("departamentoNombre").value = userData.clienteDepartamentoNombre;
}
// 3. CIUDAD: Cargar ciudades y esperar
if (userData.clienteCiudadNombre) {
await new Promise(resolve => {
const originalCallback = window.CiudadesMostrar;
window.CiudadesMostrar = function (data) {
originalCallback(data);
resolve();
};
// Llama a la función de ciudades.
Ciudades("ciudadNombre", userData.clienteDepartamentoNombre);
});
// Asigna el valor de la ciudad
document.getElementById("ciudadNombre").value = userData.clienteCiudadNombre;
}
} catch (error) {
console.error('Error al cargar las ubicaciones:', error);
}
};
NOTA:
Las funciones Paises("paisNombre"), Departamentos("departamentoNombre"),Ciudades("ciudadNombre"), son funciones que llaman una Api rest full que cargan la variables , paisNombre, departamentoNombre y ciudad nombre con los respectivas option del select.
al final queda un programa de referencia que llena los select.
¿Cómo Funciona Paso a Paso?
Envolver en una Promesa:
new Promise(resolve => { ... })crea un envoltorio. El código dentro deasync/awaitse pausa (await) hasta que se llama a la funciónresolve().Sobreescribir el Callback: Las librerías o funciones heredadas a menudo dependen de callbacks globales (ej.
window.PaisesMostrar). Temporalmente, reemplazamos esa función de callback con una propia que hace dos cosas:Llama al callback original para que siga poblando el
select.Llama a
resolve(), lo cual "despierta" elawaity permite que el código continúe.
Encadenamiento Sucesivo: Gracias a
await, cada sección del código espera pacientemente a que la anterior termine. Primero se cargan los países, luego el código se detiene. Una vez cargados, se asigna el valor, y solo entonces se pasa a la siguiente promesa para los departamentos, y así sucesivamente.
¿Por Qué Es Una Solución Robusta?
Evita el "Callback Hell": El código es plano, limpio y fácil de leer, lo que mejora la mantenibilidad.
Garantiza el Orden: Asegura que la asignación del valor de cada
selectse haga solo cuando las opciones correspondientes ya están en el DOM.Funciona con Código Existente: No necesitas reescribir tus funciones de carga (
Paises,Departamentos,Ciudades) si ya usan callbacks; simplemente las envuelves.

Comentarios
Publicar un comentario