function fillFormFromData($form, data) { // ---- helpers ---- const normalize = (v) => v == null ? "" : typeof v === "string" ? v.trim() : v; const toArray = (v) => { if (Array.isArray(v)) return v; if (!v) return []; return String(v) .split(",") .map((x) => x.trim()) .filter(Boolean); }; const ensureOption = ($select, value) => { if (value === "" || value == null) return; const exists = $select.find(`option[value="${value}"]`).length > 0; if (!exists) $select.append(new Option(value, value)); }; // Convierte "22:00:00.0000000" -> "22:00" (también maneja "HH:MM:SS" y "HH:MM") const toTimeValue = (v) => { if (!v) return ""; const s = String(v).trim(); const m = s.match(/^(\d{1,2}):(\d{2})(?::\d{2}(?:\.\d+)?)?$/); if (m) return `${m[1].padStart(2, "0")}:${m[2]}`; // HH:MM // fallback por si llega como fecha completa const d = new Date(s); if (!isNaN(d)) return d.toTimeString().slice(0, 5); // HH:MM return ""; }; // ---- binding genérico ---- for (const [campo, rawValor] of Object.entries(data)) { // Evitar índices numéricos de consultas SQL (0,1,2,...) if (!isNaN(campo)) continue; const valor = normalize(rawValor); // Buscar por id iptXxx y si no, por name="campo" const inputId = "#ipt" + campo.charAt(0).toUpperCase() + campo.slice(1); let $el = $form.find(inputId); if (!$el.length) $el = $form.find(`[name="${campo}"]`); if (!$el.length) continue; // Radios (varios inputs con el mismo name) if ($el.length > 1 && $el.is(":radio")) { $el .prop("checked", false) .filter((_, r) => $(r).val() == valor) .prop("checked", true) .trigger("change"); continue; } const $c = $el.eq(0); // Checkbox if ($c.is(":checkbox")) { $c.prop("checked", valor == 1 || valor === true || valor === "1").trigger( "change" ); continue; } // MultiSelect (Syncfusion) detectado por clase .multiselect if ($c.hasClass("multiselect")) { // obtener instancia de ej2 sin alias globales const host = $c.get(0); const inst = host?.ej2_instances?.[0]; // ej.dropdowns.MultiSelect if (inst) { inst.value = toArray(valor); inst.dataBind(); } else { // si por algún motivo aún no está instanciado, al menos deja el valor en un data-attr $c.attr("data-pending-value", toArray(valor).join(",")); } continue; } // Select normal (incluido selectpicker) if ($c.is("select")) { const multiple = $c.prop("multiple"); if (multiple) { const arr = toArray(valor); arr.forEach((v) => ensureOption($c, v)); $c.val(arr); } else { ensureOption($c, valor); $c.val(valor); } if ($c.hasClass("selectpicker")) $c.selectpicker("refresh"); $c.trigger("change"); continue; } // Input time: recorta a HH:MM if ($c.is('input[type="time"]')) { $c.val(toTimeValue(valor)).trigger("change"); continue; } if ($c.hasClass("richtexteditor")) { // Buscar instancia RTE en el contenedor #editor const host = document.querySelector("#editor"); const inst = host?.ej2_instances?.[0]; // instancia del RTE if ( inst && inst.getModuleName && inst.getModuleName() === "richtexteditor" ) { inst.value = valor || ""; inst.dataBind(); // También reflejar el valor en el textarea oculto para que FormData lo capture $c.val(valor || ""); } else { // Si aún no está montado el editor, guardo el valor pendiente $c.attr("data-pending-rte", valor || ""); } continue; } // Inputs/textarea genéricos if ($c.is("input, textarea")) { $c.val(valor).trigger("change"); continue; } // Fallback para elementos de texto (spans/divs) $c.text(valor); } }